<?php

declare(strict_types=1);

namespace App\Activity\Service;

use App\Activity\Job\SubscribeJob;
use App\Activity\Model\ActivityGoodsModel;
use App\Activity\Model\ActivityModel;
use App\Activity\Model\GroupOrder;
use App\Activity\Model\SecKillActivityRemindModel;
use App\Common\Constants\ErrorCode;
use App\Common\Constants\Stakeholder;
use App\Common\Service\Arr;
use App\Common\Service\BaseService;
use App\Common\Service\Generator;
use App\Common\Service\Random;
use App\Order\Model\KzRefundLogModel;
use App\Order\Model\OrderModel;
use App\Order\Model\WxRefundLogModel;
use App\Order\Service\Order\OrderMiniService;
use App\Order\Service\OrderGoodsService;
use App\Order\Service\OrderService;
use App\Resource\Model\CategoryModel;
use App\Resource\Model\CrdCategoryModel;
use App\Resource\Model\GoodsListModel;
use App\Resource\Model\GoodsModel;
use App\Resource\Model\HfCartModel;
use App\Resource\Model\ShopModel;
use App\Resource\Service\ShopGroupService;
use App\User\Model\MemberModel;
use App\User\Model\UserModel;
use App\User\Service\MemberService;
use Carbon\Carbon;
use Hyperf\AsyncQueue\Driver\DriverFactory;
use Hyperf\Database\Concerns\BuildsQueries;
use Hyperf\Database\Model\Builder;
use Hyperf\DbConnection\Db;
use Hyperf\Di\Annotation\Inject;
use Hyperf\Redis\RedisFactory;
use Hyperf\Utils\Context;
use Hyperf\Utils\Parallel;
use Swoole\Table;

class ActivityService extends BaseService
{

    /**
     * @Inject()
     * @var OrderGoodsService
     */
    private $orderGoodsService;

    /**
     * @Inject()
     * @var Random
     */
    private $random;

    /**
     * @Inject()
     * @var ShopGroupService
     */
    private $shopGroupService;

    /**
     * @param array $where
     * @param int $perPage
     * @param int|null $type    0团购，1限购，2专题，3接龙，4秒杀，5免单
     * @param array|string[] $field
     *
     * @return array
     */
    public function getList(array $where = [], int $perPage = 15, ?int $type = null, array $field = ['*'])
    {
        $query = ActivityModel::query();
        !empty($where['title']) && $query->whereRaw('INSTR(title, ?) > 0', [$where['title']]);
        if($type != 5){
            $status = isset($where['status']) ? $where['status'] : 1;
        }else{
            if(!in_array($where['status'], ['0', '1', '2', '3'])){
                $status = null;
            }else{
                $status = $where['status'];
            }
        }

        if($type == 4 && $status == 0){
            $query->whereIn('status', ['0','3']);
        }elseif($status != null){
            $query->where('status', '=', $status);
        }
//        !empty($where['start_time']) && $query->whereDate('begin_date', '>=', $where['start_time']);
//        !empty($where['end_time']) && $query->whereDate('end_date', '<=', $where['end_time']);
        !empty($where['start_time']) && $query->where('begin_date', '>=', $where['start_time']);
        !empty($where['end_time']) && $query->where('end_date', '<=', $where['end_time']);
        $query->where('activityType', '=', $type);
        $query->where('is_deleted', '<>', 1);
        if(!empty($where['date'])){
            $date = [trim(substr($where['date'], 0, 19)), trim(substr($where['date'], -19))];
            $query->whereBetween('begin_date', $date);
//            $query->whereBetween('begin_date', ['2020-12-12 00:00:00','2020-12-18 23:59:59']);
        }
        // 排序规则：所有活动按 sort 字段排序，sort 相同则按 id 排序
        $list = $query->latest('sort') -> latest('activityID')->paginate($perPage, $field);
        if ($list->isEmpty()) {
            return ['code' => ErrorCode::SUCCESS, 'data' => $list];
        }
        $activityIds = array_column($list->toArray()['data'], 'activityID');
        // 限购、专题、秒杀、免单活动，需要查商品数量
        if ($type == 1 || $type == 2 || $type == 4 || $type == 5) {
            $activityGoods = ActivityGoodsModel::whereIn('activityID', $activityIds)
                ->selectRaw('COUNT(goods_id) as goods_num,activityID')
                ->groupBy(['activityID'])
                ->pluck('goods_num', 'activityID')->toArray();
        }
        if ($type == 0 || $type == 1 || $type == 3 || $type == 4 || $type == 5) {
            // 获取应用门店数
            $shopIds = array_column($list->toArray()['data'], 'shop_ids', 'activityID');
            if($type == 5){
                $handleUids = array_column($list->toArray()['data'], 'updated_uid', 'activityID');
                $handleMan = UserModel::query() -> whereIn('id', $handleUids) -> pluck('username', 'id');
            }
            foreach ($list as $key => $val) {
                if ($shopIds[$val->activityID]) {
                    $list[$key]['shop_count'] = count(explode(',', $shopIds[$val->activityID]));
                } else {
                    $list[$key]['shop_count'] = 0;
                }
                if ($type == 0 || $type == 3) {
                    // 获取提货时间
                    $prams = json_decode($val['activity_prams'], true);
                    $list[$key]['th_time'] = $prams['pick_time'];
                    unset($list[$key]['activity_prams']);
                } else {
                    // 获取商品数
                    $list[$key]['goods_count'] = $activityGoods[$val->activityID] ?? 0;
                    $list[$key]['handle_man'] = $handleMan[$val['updated_uid']] ?? '';
                }
            }
        } else {
            foreach ($list as $key => $val) {
                $list[$key]['goods_count'] = $activityGoods[$val->activityID] ?? 0;
            }
        }

        return ['code' => ErrorCode::SUCCESS, 'data' => $list];
    }

    /**
     * @param int $id
     * @param int $type
     * @param array|string[] $field
     * @param array $params
     *
     * @return array
     */
    public function getInfoById(int $id, int $type, array $field = ['*'], array $params = [])
    {
        $res = ActivityModel::query()->where([
            'activityID' => $id,
            'activityType' => $type,
        ])->select($field)->first();
        if (!$res) {
            return ['code' => ErrorCode::NOT_EXIST];
        }
        $activityPrams = json_decode($res->activity_prams, true);
        switch ($type) {
            case 0:
                $thTime = explode(' - ', $activityPrams['pick_time']);
                $res->th_start_time = $thTime[0] ?? 0;
                $res->th_end_time = $thTime[1] ?? 0;
                $res->need_people = $activityPrams['group_num'] ?? 0;
                $res->number = $activityPrams['number'] ?? 0;
                $res->share_img = $activityPrams['share_img'] ?? '';
                break;
            case 1:
                $res->number = $activityPrams['number'] ?? 0;
                $res->stock = $activityPrams['stock'] ?? 0;
                break;
            case 2:
                $res->share_img = $activityPrams['share_img'] ?? '';
                break;
            case 3:
                $thTime = explode(' - ', $activityPrams['pick_time']);
                $res->th_start_time = $thTime[0] ?? 0;
                $res->th_end_time = $thTime[1] ?? 0;
                $res->number = $activityPrams['number'] ?? 0;
                $res->share_img = $activityPrams['share_img'] ?? '';
                $res->commission = $activityPrams['commission'] ?? '';
                break;
            case 4:
            case 5:
                // 获取活动绑定的所有店铺名称
                $shopIds = explode(',',$res -> shop_ids);
                $shopNames = ShopModel::query()
                    -> whereIn('shop_id', $shopIds)
                    -> when($params['shop_name'] ?? 0, function ($query, $shopName) {
                        return $query->whereRaw('INSTR(shop_name, ?) > 0', [$shopName]);
                    })
                    -> get('shop_name');

                // 获取活动的商品信息
                $query = ActivityGoodsModel::query();
                if (!empty($params['goods_title'])) {
                    if (is_numeric($params['goods_title'])) {
                        $query->whereRaw('INSTR(goods_id, ?) > 0', [$params['goods_title']]);
                    } else {
                        $query->whereRaw('INSTR(goods_title, ?) > 0', [$params['goods_title']]);
                    }
                }
                $shopGoods = $query
                    -> from('store_activity_goods as ag')
                    -> leftJoin('store_goods as goods', 'ag.goods_id', '=', 'goods.id')
                    -> select(['ag.goods_id','ag.goods_title','ag.goods_cate','ag.goods_logo','ag.goods_spec','ag.costprice','ag.price_selling','ag.per_can_buy_num','ag.stock','ag.commission','ag.free_num','ag.need_invited_num', 'goods.status', 'goods.lsy_unit_name'])
                    -> where('activityID', $id)
                    -> get();


                $res -> shop_info = $shopNames;
                $res -> goods_info = $shopGoods;
                break;
        }

        return ['code' => ErrorCode::SUCCESS, 'data' => $res];
    }

    /**
     * @param array $params
     * @param int $type 0团购，1限购，2专题
     *
     * @return array
     */
    public function add(array $params, int $type = 0)
    {
        $dataArr = $this -> attributeIntegration($params, $type);
        $res = $this->insertOrUpdate('add', $params, $type, $dataArr);
        if ($res['code']) {
            return ['code' => $res['code'], 'msg' => $res['msg'] ?? []];
        }

        return ['code' => ErrorCode::SUCCESS, 'data' => [], 'info' => $res['info']];
    }

    /**
     * @param array $params
     * @param int $type 活动类型
     *
     * @return array
     */
    public function update(array $params, int $type)
    {
        if($type == 4 || $type == 5){
            // 秒杀活动只允许未开始的条目进行编辑
            $exist = ActivityModel::query()->where(['activityID' => $params['id'], 'activityType' => $type ,'status' => 1])->exists();
            if(!$exist){
                return ['code' => ErrorCode::ACTIVITY_CAN_NOT_EDIT];
            }
        }else{
            $exist = ActivityModel::query()->where(['activityID' => $params['id'], 'activityType' => $type])->exists();
            if (!$exist) {
                return ['code' => ErrorCode::NOT_EXIST];
            }
        }

        $dataArr = $this -> attributeIntegration($params, $type);
        $res = $this->insertOrUpdate('update', $params, $type, $dataArr);
        if ($res['code']) {
            return ['code' => $res['code'], 'msg' => $res['msg'] ?? []];
        }

        return ['code' => ErrorCode::SUCCESS, 'data' => [], 'info' => $res['info']];
    }

    /**
     * 团购活动的自提/发货时间需在活动结束的24小时后（目前弃用）
     * @param array $item
     *
     * @return bool
     */
    private function delierTime(array $item)
    {
        // 时间二次检测
        $end_time = $item['end_time'];
        $th_start_time = $item['th_start_time'];
        $canThTime = date('Y-m-d H:i:s', strtotime($end_time . "+1 day"));
        if ($th_start_time <= $canThTime) {
            return false;
        }

        return true;
    }

    /**
     * 整合活动属性参数
     * @param $params
     * @param $type
     * @return array
     */
    private function attributeIntegration(array $params, int $type)
    {
        switch ($type) {
            case 0:
                $dataArr = [
                    'pick_time' => $params['th_start_time'] . ' - ' . $params['th_end_time'],
                    'group_num' => $params['need_people'],  // 几人团
                    'number' => $params['number'],    // 作弊数，虚拟人数
                    'share_img' => $params['share_img'],
                ];
                break;
            case 1:
                $dataArr = [
                    'number' => $params['number'],
                    'stock' => $params['stock'],
                ];
                break;
            case 2:
                $dataArr = ['share_img' => $params['share_img'],];
                break;
            case 3:
                $dataArr = [
                    'pick_time' => $params['th_start_time'] . ' - ' . $params['th_end_time'],
                    'number' => $params['number'],    // 作弊数，虚拟人数
                    'share_img' => $params['share_img'],
                ];
                break;
            case 4:
                if($params['e_time'] == '00:00:00'){
                    $params['e_time'] = '23:58:00';
                }
                $dataArr = [
                    'start_time' => $params['s_time'],
                    'end_time' => $params['e_time'],
                ];
                break;
            case 5:
                $dataArr = [
                    'first_invite_time' => $params['first_invite_time'],
                    'is_second' => $params['is_second'],
                    'second_activation' => $params['second_activation'],
                    'second_invite_time' => $params['second_invite_time'],
                    'special_image' => $params['special_image'],
                    'share_image' => $params['share_image'],
                    'share_text' => $params['share_text'],
                ];
                break;
            default:
                $dataArr = [];
                break;
        }

        return $dataArr;
    }

    /**
     * @param string $startTime 当前操作活动的活动开始时间
     * @param string $endTime   当前操作活动的活动结束时间
     * @param int $activityId   当前操作活动的活动 ID
     * @return array|bool
     */
    public function detectActiveStoreTimeConflict(string $startTime, string $endTime, int $activityId)
    {
        $info = ActivityModel::query() -> where('activityID', $activityId) -> first(['status', 'begin_date', 'end_date', 'shop_ids', 'activityType']);
        if(!$info){
            return ['code' => ErrorCode::NOT_EXIST];
        }
        $list = ActivityModel::query()
            ->where('activityType', $info -> activityType)
            ->where('is_deleted', 0)
            ->whereIn('status', [1, 2])
            ->whereNotIn('activityID', [$activityId])
//            ->whereDate('begin_date', $currentDate)
            ->get(['activityID', 'begin_date', 'end_date', 'shop_ids']);

        if(!$list -> isEmpty()){
            // 拼接活动的时间，并检测时间是否重合
            foreach ($list as $key => $val){
                $isTimeCross = $this -> shopGroupService -> isTimeCross($startTime, $endTime, $val['begin_date'], $val['end_date']);
                // 时间冲突
                if($isTimeCross){
                    // 判断两个活动有无相同的店铺，若有则不能变更状态
                    $firstArr = explode(',', $info -> shop_ids);
                    $secondArr = explode(',', $val['shop_ids']);
                    $checkSameShopId = array_intersect($firstArr, $secondArr);
                    if(!empty($checkSameShopId)){
                        // 有冲突
                        return false;
                    }
                }
            }
        }
        // 没有冲突
        return true;
    }

    /**
     * @param string $method
     * @param array $params
     * @param int $type 0团购，1限购，2专题，3接龙，4秒杀
     * @param array $dataArr
     *
     * @return array
     */
    private function insertOrUpdate(string $method, array $params, int $type, array $dataArr)
    {
        try {
            DB::transaction(function () use ($method, $params, $type, $dataArr) {
                if ($method == 'update') {
                    ActivityModel::query()->where([
                        'activityID' => $params['id'],
                        'activityType' => $type,
                    ])->update([
                        'title' => $params['title'] ?? '',
                        'begin_date' => $params['start_time'] ?? Carbon::today()->toDateTimeString(),
                        'end_date' => $params['end_time'] ?? Carbon::parse($params['start_time'])->endOfDay()->toDateTimeString(),
                        'sort' => $params['sort'] ?? 0,
                        'image' => $params['activity_img'] ?? '',
                        'shop_ids' => $params['shop_id'] ?? '',
                        'activity_prams' => json_encode($dataArr),
                        'updated_uid' => $this->jwt->setScene('default')->getParserData()['uid'],
                    ]);
                } else {
                    if($type == 4){
                        // 如果是秒杀活动，需判断当前时间是否处于活动时间中
                        $checkTime = $this -> checkTime($params['start_time'], $dataArr['start_time'], $dataArr['end_time']);
                        $actStatus = $checkTime ? 2 : 1;
                    }
                    if($type == 5){
                        // 如果是免单活动，需判断当前时间是否处于活动时间中
                        $checkTime = $this -> checkTime('', $params['start_time'], $params['end_time']);
                        $actStatus = $checkTime ? 2 : 1;
                    }

                    $activityInfo = ActivityModel::query()->create([
                        'title' => $params['title'] ?? '',
                        'begin_date' => $params['start_time'] ?? Carbon::today()->toDateTimeString(),
                        'end_date' => $params['end_time'] ?? Carbon::parse($params['start_time'])->endOfDay()->toDateTimeString(),
                        'sort' => $params['sort'] ?? 0,
                        'image' => $params['activity_img'] ?? '',
                        'activityType' => $type,
                        'status' => $actStatus ?? 1,
                        'shop_ids' => $params['shop_id'] ?? '',
                        'activity_prams' => json_encode($dataArr),
                        'updated_uid' => $this->jwt->setScene('default')->getParserData()['uid'],
                    ]);
                }
                // 活动商品信息
                $goods = json_decode($params['goods_id'], true);

                // 运营要求：团购活动、接龙活动添加的商品，该商品状态修改为下架
                if($type == 0 || $type == 3){
                    $groupGoods = array_column($goods,'goods_id');
                    GoodsModel::query()->whereIn('id',$groupGoods)->update(['status' => 0]);
                }
                $activityId = ($method == 'update') ? $params['id'] : $activityInfo->activityID;
                $activityGoods = $this -> integration($goods, $method, $params, $type, $activityId);
                if($type == 4){
                    // 若有 shop_ids 参数，则为秒杀活动添加商品操作。今天的秒杀活动中，相同店铺添加过的商品，新增活动则不能再次添加。
                    $notInGoodsList = $this -> checkGoods($params['shop_id'], $params['id'] ?? null);
                    if(!empty($notInGoodsList)){
                        // 对比提交的商品中有无和当前检测结果中冲突的商品
                        $submitGoodsIds = array_column($activityGoods, 'goods_id');
                        $checkGoods = array_intersect($notInGoodsList,$submitGoodsIds);
                        if(!empty($checkGoods)){
                            $activityGoodsIndex = array_column($activityGoods,null,'goods_id');
                            $GoodsTitleStr = '';
                            foreach($checkGoods as $key => $val){
                                $GoodsTitleStr .= $activityGoodsIndex[$val]['goods_title'].'，';
                            }
                            $GoodsTitleStr .= '这些商品今日已在相同店铺中参与秒杀，无法再次销售！';
                            throw new \Exception($GoodsTitleStr, 4121);
                        }
                    }
                }

                if($type == 5){
                    // TODO 如果时免单活动，需判断当前操作的活动的时间是否和其它活动店铺在时间上冲突
                    $isConflict = $this -> detectActiveStoreTimeConflict($params['start_time'], $params['end_time'], $activityId);
                    if(!$isConflict) throw new \Exception('当前活动所选店铺存在和目标时间冲突', 4333);
                }

                if ($method == 'update') {
                    // 目前活动商品在编辑时只能增量添加或编辑，不能删除
                    foreach ($activityGoods as $key => $val) {
                        ActivityGoodsModel::query()->updateOrInsert(['activityID' => $params['id'], 'goods_id' => $val['goods_id'], 'is_deleted' => 0], $val);
                    }
                    //若更新活动，则删除该活动对应的附近店铺 Redis 数据
                    if($type == 0){
                        $this->resourceRedis->del('nearShop-actId:' . $params['id']);
                    }
                } else {
                    ActivityGoodsModel::query()->insert($activityGoods);
                }
                Context::set('activityId', $activityInfo->activityID ?? $params['id']);
            });
        } catch (\Exception $e) {
            if ($e->getCode() == ErrorCode::GOODS_IN_ACTIVITY) {
                return ['code' => ErrorCode::GOODS_IN_ACTIVITY];
            } elseif ($e->getCode() == ErrorCode::PRICE_INVALID) {
                return ['code' => ErrorCode::PRICE_INVALID];
            } elseif($e->getCode() == 4121){
                return [
                    'code' => 4121,
                    'msg' => $e->getMessage()
                ];
            } elseif ($e->getCode() == 4333){
                return [
                    'code' => 4333,
                    'msg' => $e->getMessage()
                ];
            } else {
                return ['code' => ErrorCode::NOT_IN_FORCE];
            }
        }

        // 添加团购活动, 启动定时成团检测
        if ($type == 0) {
            $acId = Context::get('activityId');
            $join = [
                'acId' => $acId,
                'begin' => strtotime($params['start_time']),
                'end' => strtotime($params['end_time']),
                'need' => $params['need_people']
            ];
            $this->groupRedis->hSet('group:timer', (string)$acId, json_encode($join));
        }
        return ['code' => ErrorCode::SUCCESS, 'data' => ['type' => $type], 'info' => ['target_id' => Context::get('activityId')]];
    }

    /**
     * 整合活动商品数据
     * @param $goods
     * @param $method
     * @param $params
     * @param $type
     * @param $activityId
     * @return array
     *
     * @throws \Exception
     */
    private function integration($goods, $method, $params, $type, $activityId)
    {
        $activityGoods = [];
        foreach ($goods as $key => $val) {
            if ($type != 2) {
                // 专题活动和分享免单活动不要求活动价要小于原价
                if ($type != 5 && ($val['group_price'] >= $val['crossed_price'])) {
                    throw new \Exception('活动价必须小于划线价', ErrorCode::PRICE_INVALID);
                }
                if ($type == 4 && $val['per_can_buy_num'] <= 0) {
                    throw new \Exception('每人限购秒杀数量至少为 1', ErrorCode::PARAMS_INVALID);
                }
                $activityGoods[$key]['costprice'] = $val['crossed_price'] ?? '';
                $activityGoods[$key]['price_selling'] = $val['group_price'];
                $activityGoods[$key]['stock'] = $val['stock'];
                $activityGoods[$key]['commission'] = $val['commission'] ?? 0.00;
                $activityGoods[$key]['spell_num'] = $val['spell_num'] ?? 0;
                $activityGoods[$key]['need_invited_num'] = $val['need_invited_num'] ?? 0;
                // 秒杀活动商品，需要添加每人可秒杀数量
                if($type == 4) $activityGoods[$key]['per_can_buy_num'] = $val['per_can_buy_num'] ?? 1;
                // 免单活动商品，免单基数
                if($type == 5) $activityGoods[$key]['free_num'] = $val['free_num'] ?? 0;
            } else {
                //添加专题活动商品时，若该商品已在团购和限购中，则不能再被添加
                $isGoodsInOtherActivity = ActivityGoodsModel::query()->whereIn('activityType', [0, 1])
                    ->where('goods_id', $val['goods_id'])->exists();
                if ($isGoodsInOtherActivity) {
                    throw new \Exception('检测到所选商品已存在于其他活动中，不能再被添加到专题活动', ErrorCode::GOODS_IN_ACTIVITY);
                }
            }
            if($type == 4){
                $goodsField = ['title', 'logo', 'crd_cate_id'];
            }else{
                $goodsField = ['title', 'logo', 'cate_id', 'lsy_unit_name', 'status'];
            }

            $goodsInfo = GoodsModel::query()->where(['id' => $val['goods_id']])->select($goodsField)->first();
            $crdGoodsCate = CrdCategoryModel::query()->where('id', $goodsInfo['crd_cate_id'])->value('title');
            $goodsSpec = GoodsListModel::query()->where(['goods_id' => $val['goods_id']])->value('goods_spec');
            if ($method == 'update') {
                $activityGoods[$key]['activityID'] = $params['id'];
            } else {
                $activityGoods[$key]['activityID'] = $activityId;
            }

            $activityGoods[$key]['goods_id'] = $val['goods_id'];
            $activityGoods[$key]['goods_title'] = $goodsInfo->title;
            $activityGoods[$key]['goods_logo'] = $goodsInfo->logo;
            $activityGoods[$key]['goods_spec'] = $goodsSpec;
            $activityGoods[$key]['activityType'] = $type;
            $activityGoods[$key]['goods_cate'] = $crdGoodsCate ?? CategoryModel::query()->where('id', $goodsInfo['cate_id'])->value('title') ?? '';
            switch ($type) {
                case 0:
                case 3:
                    // 商品分组：小跑好物拼团
                    $activityGoods[$key]['group_id'] = 1;
                    break;
                case 1:
                    // 商品分组：限时抢购
                    $activityGoods[$key]['group_id'] = 3;
                    break;
                case 2:
                    // 商品分组：普通商品
                    $activityGoods[$key]['group_id'] = 0;
                    break;
                case 4:
                    // 商品分组：秒杀商品
                    $activityGoods[$key]['group_id'] = 4;
                    break;
                case 5:
                    // 商品分组：普通商品
                    $activityGoods[$key]['group_id'] = 5;
                    break;
            }
        }

        return $activityGoods;
    }

    /**
     *
     * 1、筛选出今日所有的秒杀活动。
     * 2、筛选出和当前要添加的活动所选店铺中的任何一家重复的所有活动id。
     * 3、上述活动列表中，每个活动所选商品，当前要添加的活动则不能再添加。
     */
    public function checkGoods($shopIds, $editSign)
    {
        $todayDate = date('Y-m-d', time());
        $shopIdsArr = explode(',', $shopIds);
        $in = [
            ['shop_ids', $shopIdsArr],
        ];
        $activityIdsWithCrossShop = ActivityModel::query()
            ->where('is_deleted', 0)
            ->where('activityType', 4)
            ->when($editSign ?? 0, function ($query, $editSign) {
                return $query->whereNotIn('activityID', [$editSign]);
            })
            ->whereIn('status', [1,2])
            ->whereRaw('INSTR(begin_date, ?) > 0', [$todayDate])
            ->Where(function ($query) use ($in){
                foreach ($in as $k => $v) {
                    foreach($v[1] as $key => $val){
                        $query->orWhere($v[0],'regexp', $val);
                    }
                }
                return $query;
            })
            ->pluck('activityID')->toArray();

        if(!empty($activityIdsWithCrossShop)){
            $prohibitedGoods = ActivityGoodsModel::query()
                ->whereIn('activityID', $activityIdsWithCrossShop)
                ->where('is_deleted', 0)
                ->pluck('goods_id')->toArray();
            $prohibitedGoods = array_unique($prohibitedGoods);
        }else{
            $prohibitedGoods = [];
        }

        return $prohibitedGoods;
    }

    /**
     * @param int $ids
     * @param int $type
     *
     * @return array
     */
    public function delete(int $ids, int $type)
    {
        try {
            DB::transaction(function () use ($ids, $type) {
                $res = ActivityModel::query()->where([
                    'activityID' => $ids,
                    'activityType' => $type,
                ])->update(['is_deleted' => 1]);
                $res2 = ActivityGoodsModel::query()->where(['activityID' => $ids])->update(['is_deleted' => 1]);
                if (!$res) {
                    throw new \Exception('未生效', ErrorCode::NOT_IN_FORCE);
                }
            });
        } catch (\Exception $e) {
            return ['code' => ErrorCode::NOT_IN_FORCE];
        }
        if ($type == 0) {
            $this->groupRedis->hDel('group:timer', (string)$ids);
            $this->resourceRedis->del('nearShop-actId:' . $ids);
        }
        return ['code' => ErrorCode::SUCCESS, 'data' => ['type' => $type], 'info' => ['target_id' => $ids]];
    }

    /**
     * 拼团活动数据分析
     *
     * @param array $params
     * @param array|string[] $field
     *
     * @return \Hyperf\Database\Concerns\BuildsQueries|\Hyperf\Database\Model\Builder|mixed
     * @return BuildsQueries|Builder|mixed
     */
    public function activityGroupList(array $params, array $field = ['*'])
    {
        $list = ActivityModel::query()
            ->when($params['title'] ?? 0, function ($query, $title) {
                return $query->whereRaw('INSTR(title, ?) > 0', [$title]);
            })
            ->when($params['start'] ?? 0, function ($query, $start) {
                return $query->whereBetween('begin_date', $start);
            })
            ->when($params['end'] ?? 0, function ($query, $end) {
                return $query->whereBetween('end_date', $end);
            })
            ->with('activityGoods:activityID,id')
            ->select($field)
            ->where('activityType', 0)
            ->latest('create_at')
            ->when($params['perpage'] ?? 0, function ($query, $perPage) {
                return $query->paginate((int)$perPage);
            }, function ($query) {
                return $query->get();
            });

        $activityGoodsId = [];
        foreach ($list as $k => $v) {
            $list[$k]['acgId'] = array_column($v->activityGoods->toArray(),
                'id');
            $activityGoodsId = array_merge($activityGoodsId,
                array_column($v->activityGoods->toArray(), 'id'));
        }
        $acGoodsInfo
            = $this->orderGoodsService->getGoodsInfoByActivityGoodsId($activityGoodsId);
        foreach ($list as $k => $v) {
            $list[$k]['amount'] = 0;
            $list[$k]['quantity'] = 0;
            $list[$k]['group_member_count'] = 0;
            $list[$k]['new_member_count'] = 0;
            $list[$k]['old_member_count'] = 0;
            $list[$k]['of_sales'] = (1 + $k) . '%';
            foreach ($acGoodsInfo as $kk => $vv) {
                if (in_array($vv['activity_goods_id'], $v['acgId'])) {
                    $list[$k]['amount'] += $vv['amt'];
                    $list[$k]['quantity'] += $vv['number'];
                    $list[$k]['group_member_count'] += $vv['groupPerNum'];
                    $list[$k]['old_member_count'] += $vv['groupPerNum'];
                }
            }
            if (!$list[$k]['amount']) {
                $list[$k]['of_sales'] = '0%';
            }
            unset($v['acgId'], $v->activityGoods);
        }

        return $list;
        //activityID ===> store_activity_goods prk id

    }

    /**
     * @param int $id
     * @param string $shopId
     * @param int $type  活动类型：0团购，1限购，2专题，3皆空，4秒杀
     *
     * @return array
     */
    public function activityBindShop(int $id, string $shopId, int $type)
    {
        $res = ActivityModel::where([
            'activityID' => $id,
            'activityType' => $type,
        ])->update(['shop_ids' => $shopId]);
        if ($res) {
            return ['code' => ErrorCode::SUCCESS, 'data' => [], 'info' => ['target_id' => $id, 'remarks' => 'shop_ids:'.$shopId]];
        } else {
            return ['code' => ErrorCode::NOT_IN_FORCE];
        }

    }


    /**
     * 支付成功后开团
     *
     * @param int $activityId
     * @param int $mid
     * @param string $orderNo
     * @return array
     */
    public function activityGroupStart(int $activityId, int $mid, string $orderNo)
    {
        //获取随机团号(正在进行中)
        $groupNumber = $this->random::character(8);
        // 用户信息
        $mb = MemberModel::query()->find($mid, ['nickname', 'face_url']);
        //将当前用户加入团中
        $now = time();
        $join = [
            'mid' => $mid,
            'orderNo' => $orderNo,
            'activityId' => $activityId,
            'nickname' => $mb['nickname'],
            'avatar' => $mb['face_url'],
            'time' => $now
        ];
        $res = $this->groupRedis->zAdd($groupNumber, $now, json_encode($join));//默认添加创团人为团长
        if ($res) {
            $this->groupRedis->sAdd("group{$activityId}:ing", $groupNumber);// 拼团中
            $this->groupRedis->sAdd("share-{$mid}-{$activityId}", $orderNo);// 待分享
            $this->groupRedis->hSet("groupNum:orderNo", $orderNo, $groupNumber);// 订单号 <=> 团号
            // 持久化
            $this->createGroupOrderMap($orderNo, $groupNumber);
            return ['code' => 1, 'msg' => '开团成功', 'data' => ['groupNumber' => $groupNumber]];
        }
        return ['code' => 0, 'errorCode' => ErrorCode::SYSTEM_INVALID, 'msg' => '开团失败'];
    }

    /**
     * 支付成功后参团
     *
     * @param int $activityId
     * @param string $groupNumber 团号
     * @param int $mid
     * @param string $orderNo
     * @return array
     */
    public function activityGroupJoin(int $activityId, string $groupNumber, int $mid, string $orderNo)
    {
        $groupCount = $this->groupRedis->zCard($groupNumber);// 成员数
        if (!$groupCount) {
            return ['code' => 0, 'errorCode' => ErrorCode::SYSTEM_INVALID, 'msg' => '该团不存在'];
        }
        $needPeople = $this->groupNeedPeople($activityId)['group_num'];
        if ($groupCount < $needPeople) {
            $exist = 0;
            foreach ($this->groupRedis->zRange($groupNumber, 0, -1) as $k => $v) {
                if (json_decode($v, true)['mid'] == $mid) {
                    $exist = 1;
                }
            }
            if ($exist) {
                return $this->activityGroupStart($activityId, $mid, $orderNo);
                //已经参加过团
                //return ['code' => 0, 'errorCode' => ErrorCode::SYSTEM_INVALID, 'msg' => '该团已参加'];
            } else {
                $mb = MemberModel::query()->find($mid, ['nickname', 'face_url']);
                $join = [
                    'mid' => $mid,
                    'orderNo' => $orderNo,
                    'activityId' => $activityId,
                    'nickname' => $mb['nickname'],
                    'avatar' => $mb['face_url'],
                    'time' => time()
                ];
                $res = $this->groupRedis->zAdd($groupNumber, time(), json_encode($join));
                if ($res) {
                    $this->groupRedis->hSet("groupNum:orderNo", $orderNo, $groupNumber);// 订单号 <=> 团号
                    // 持久化
                    $this->createGroupOrderMap($orderNo, $groupNumber);
                    $list = $this->groupRedis->zRange($groupNumber, 0, -1);
                    if (count($list) == $needPeople) {
                        // 满团后添加到成功的集合
                        $this->groupRedis->sAdd("group{$activityId}:success", $groupNumber);
                        // 删除拼团中的数据
                        $this->groupRedis->sRem("group{$activityId}:ing", $groupNumber);
                        // 成团后删除待分享
                        foreach ($list as $k => $v) {
                            $info = json_decode($v, true);
                            $this->groupRedis->sRem("share-{$info['mid']}-{$info['activityId']}", $info['orderNo']);
                        }
                    } else {
                        $this->groupRedis->sAdd("share-{$mid}-{$activityId}", $orderNo);
                    }
                    return ['code' => 1, 'msg' => '参团成功', 'data' => ['groupNumber' => $groupNumber]];
                }
                return ['code' => 0, 'errorCode' => ErrorCode::SYSTEM_INVALID, 'msg' => '参团失败'];
            }
        } else {
            //此团已满 , 重新开团
            return $this->activityGroupStart($activityId, $mid, $orderNo);
            //return ['code' => 0, 'errorCode' => ErrorCode::SYSTEM_INVALID, 'msg' => '该团已满'];
        }
    }

    /**
     * 可参加的拼团列表
     * @param int $activityId
     * @param int $mid
     * @return array
     */
    public function canJoinGroup(int $activityId, int $mid)
    {

        $res = $this->groupRedis->sRandMember("group{$activityId}:ing", 20);
        if (!$res) {
            return ['code' => 0, 'errorCode' => ErrorCode::SYSTEM_INVALID, 'msg' => '没有可参与的拼团'];
        }

        $needPeople = $this->groupNeedPeople($activityId);
        // 剩余时间
        $overTime = strtotime($needPeople['date'][1]) - time();
        $data = [];
        foreach ($res as $k => $v) {
            $groupCount = count($list = $this->groupRedis->zRange($v, 0, -1));// 成员数
            foreach ($list as $kk => $vv) {
                if (json_decode($vv, true)['mid'] == $mid) {
                    continue 2;
                }
            }
            foreach ($list as $kk => $vv) {
                $data[$k]['groupNumber'] = $v;
                $data[$k]['needPeople'] = $needPeople['group_num'];
                $data[$k]['overTime'] = $overTime;
                $data[$k]['diffPeople'] = $needPeople['group_num'] - $groupCount;
                $data[$k]['member'][] = json_decode($vv, true);
            }
        }
        return ['code' => 1, 'msg' => '获取可参团列表成功', 'data' => array_values($data)];
    }

    /**
     * 拼团信息
     * @param string $groupNumber
     * @return array
     */
    public function getGroupInfoByGroupNum(string $groupNumber)
    {
        $group = $this->groupRedis->zRange($groupNumber, 0, -1);
        if ($group) {
            $activityId = json_decode($group[0], true)['activityId'];
            $needPeople = $this->groupNeedPeople((int)$activityId);
            // 剩余时间
            $overTime = strtotime($needPeople['date'][1]) - time();
            $groupCount = $this->groupRedis->zCard($groupNumber);// 成员数
            $data = [];
            foreach ($group as $k => $v) {
                $data['groupNumber'] = $groupNumber;
                $data['activityId'] = $activityId;
                $data['goodsTitle'] = $needPeople['goods_title'];
                $data['activityGoodsId'] = $needPeople['activity_goods_id'];
                $data['goodsIntroduction'] = $needPeople['introduction'];
                $data['shareImg'] = $needPeople['share_img'];
                $data['needPeople'] = $needPeople['group_num'];
                $data['overTime'] = $overTime;
                $data['diffPeople'] = $needPeople['group_num'] - $groupCount;
                $data['member'][$k] = json_decode($v, true);
            }
            return ['code' => 1, 'msg' => '获取拼团信息成功', 'data' => $data];
        }
        return ['code' => 0, 'errorCode' => ErrorCode::SYSTEM_INVALID, 'msg' => '拼团不存在'];
    }


    /**
     * 拼团活动成团人数
     * @param int $activityId
     * @return mixed
     */
    public function groupNeedPeople(int $activityId)
    {
        $activity = ActivityModel::query()
            ->from('store_activity as ac')
            ->leftJoin('store_activity_goods as ag', 'ac.activityID', '=', 'ag.activityID')
            ->where(['ac.activityID' => $activityId])
            ->first(['ac.activity_prams', 'ac.begin_date', 'ac.end_date', 'ag.id as activity_goods_id', 'ag.goods_id', 'ag.goods_title']);
        $introduction = GoodsModel::query()->where(['id' => $activity['goods_id']])->value('introduction');
        return [
            'group_num' => json_decode($activity['activity_prams'], true)['group_num'] ?? 3,
            'share_img' => json_decode($activity['activity_prams'], true)['share_img'] ?? '',
            'date' => [$activity['begin_date'], $activity['end_date']],
            'activity_goods_id' => $activity['activity_goods_id'],
            'goods_title' => $activity['goods_title'],
            'introduction' => $introduction,
        ];
    }


    /**
     * 好物拼团推荐
     * @param array $param
     * @param array|string[] $field
     * @return BuildsQueries|Builder|\Hyperf\Database\Query\Builder|mixed
     */
    public function getRecommendGroup(array $param, array $field = ['*'])
    {
        $where = [
            ['act.status', '=', Stakeholder::ACTIVITY_ENABLE_STATUS],
            ['act.end_date', '>', date('Y-m-d H:i:s')],
            ['act.activityType', '=', Stakeholder::ACTIVITY_GROUP]
        ];
        $group = ActivityModel::query()
            ->from('store_activity as act')
            ->leftJoin('store_activity_goods as ag', 'act.activityID', '=', 'ag.activityID')
            ->select($field)
            ->where($where)
            ->whereRaw('FIND_IN_SET(?, act.shop_ids)', [$param['shop_id']])
            ->latest('act.sort')
            ->when($param['perpage'] ?? 0, function ($query, $perPage) {
                return $query->paginate((int)$perPage);
            }, function ($query) {
                return $query->get();
            });
        foreach ($group as $k => $v) {
            $group[$k]['group_num'] = json_decode($v['activity_prams'], true)['group_num'];
            unset($v['activity_prams']);
        }

        return $group;
    }

    /**
     * 申请退款后清除团购信息
     * @param int $activityId
     * @param int $mid
     * @param string $orderNo
     * @return array
     */
    public function refundRemoveGroupInfo(int $activityId, int $mid, string $orderNo)
    {
        try {
            $redis = $this->groupRedis;
            $groupNumber = $redis->hGet('groupNum:orderNo', $orderNo);
            if (!$groupNumber) {
                return ['code' => 1, 'msg' => '清除拼团信息成功'];
            }

            $memberList = $redis->zRange($groupNumber, 0, -1);
            $needPeople = $this->groupNeedPeople($activityId)['group_num'];

            $redis->sRem("share-{$mid}-{$activityId}", $orderNo); // 删除待分享
            $redis->hDel('groupNum:orderNo', $orderNo);          // 删除订单映射

            if ($needPeople > count($memberList)) {
                // 未满团
                $redis->sRem("group{$activityId}:ing", $groupNumber); // 删除拼团中
            } else {
                // 满团
                foreach ($memberList as $k => $v) {
                    if (json_decode($v, true)['mid'] == $mid) {
                        $redis->zRem($groupNumber, $v); // 删除成员
                        $redis->sRem("group{$activityId}:success", $groupNumber); // 删除成功的团
                        $this->groupRedis->sAdd("group{$activityId}:ing", $groupNumber);// 添加到拼团中
                    }
                }
            }
        } catch (\Exception $e) {
            return ['code' => 0, 'errorCode' => ErrorCode::SYSTEM_INVALID, 'msg' => '清除拼团信息失败:' . $e->getMessage()];
        }
        return ['code' => 1, 'msg' => '清除拼团信息成功'];

    }

    // 订单团号映射
    protected function createGroupOrderMap(string $orderNo, string $groupNumber)
    {
        GroupOrder::query()->create(['order_no' => $orderNo, 'group_num' => $groupNumber]);
    }

    /**
     * 拼团成功播报
     * @param int $activityId
     * @param int $mid
     * @return array
     */
    public function broadcast(int $activityId, int $mid)
    {
        // 成功的团
        $iterator = null;
        $broad = [];
        $broadcast = [];
        while ($groupNumArr = $this->groupRedis->sScan("group{$activityId}:success", $iterator)) {
            foreach ($groupNumArr as $k => $v) {
                $list = $this->groupRedis->zRange($v, 0, -1);
                foreach ($list as $kk => $vv) {
                    $memb = json_decode($vv, true);
                    if ($memb['mid'] == $mid) {
                        continue;
                    }
                    array_push($broad, $memb);
                }
            }
        }

        if (!empty($broad)) {
            Carbon::setLocale('zh');
            foreach ($broad as $k => $v) {
                $time[$k] = $v['time'];
            }
            array_multisort($time, SORT_DESC, $broad);
            $broad = array_slice(array_column($broad, null, 'mid'), 0, 15);
            foreach ($broad as $k => $v) {
                $broadcast[$k]['avatar'] = $v['avatar'];
                $broadcast[$k]['tips'] = $v['nickname'] . ' ' . Carbon::parse($v['time'])->diffForHumans() . '拼团成功';
            }
            $broadcast = array_values($broadcast);
        }
        return $broadcast;
    }

    /**
     * @param $acticityId
     * @return array
     */
    public function getShopInfoByActivityId(int $acticityId)
    {
        $shopIds = ActivityModel::query()->where('activityID', $acticityId)->value('shop_ids');
        if (!$shopIds) {
            return ['code' => ErrorCode::SUCCESS, 'data' => []];
        }
        $shopIdsArr = explode(',', $shopIds);
        $shop = ShopModel::query()
            ->whereIn('shop_id', $shopIdsArr)
            ->where('status', 1)
            ->select(['shop_id', 'shop_name', 'shop_desc', 'province', 'city', 'country', 'address', 'shop_tel', 'longitude', 'latitude'])
            ->get();
        $shopInfo = [];
        foreach ($shop as $key => $val) {
            $shopInfo[$key]['shop_id'] = $val['shop_id'];
            $shopInfo[$key]['shop_name'] = $val['shop_name'];
            $shopInfo[$key]['shop_desc'] = $val['shop_desc'];
            $shopInfo[$key]['shop_tel'] = $val['shop_tel'];
            $shopInfo[$key]['longitude'] = $val['longitude'];
            $shopInfo[$key]['latitude'] = $val['latitude'];
            $shopInfo[$key]['shop_address'] = $this->getAddress($val['province'], $val['city'], $val['country'], $val['address']);
        }
        return ['code' => ErrorCode::SUCCESS, 'data' => $shopInfo];
    }

    /** 根据店铺ID获取接龙商品
     * @param array $where
     * @param array $field
     * @param int $perpage
     * @param int $shop_id
     * @param string $title
     * @return array
     */
    public function getActivityListByShopId(array $where, array $field = ['*'], int $perpage = 10, int $shop_id = 1, string $title = '')
    {
        $query = ActivityModel::query();
        if (!empty($title)) {
            $query->whereRaw('INSTR(g.title, ?) > 0', [$title]);
        }
        $list = $query
            ->join('store_activity_goods as ag', 'store_activity.activityID', '=', 'ag.activityID')
            ->join('store_goods as g', 'ag.goods_id', '=', 'g.id')
            ->orderBy('store_activity.sort', 'desc')
            ->orderBy('store_activity.end_date', 'desc')
            ->whereRaw('FIND_IN_SET(?,store_activity.shop_ids)', [$shop_id])
            ->where($where)
            ->where('ag.stock', '>', 0)
            ->where('store_activity.end_date', '>', date('Y-m-d H:i:s'))
            ->where('store_activity.begin_date', '<', date('Y-m-d H:i:s'))
            ->select($field)
            ->Paginate($perpage);
        $activity_goods_id = [];
        foreach ($list as $k => $v) {
            $list[$k]['activity_prams'] = json_decode($v['activity_prams'], true);
            $list[$k]['spell_num'] = $list[$k]['activity_prams']['number'] + $list[$k]['spell_num'];
            $activity_goods_id[] = $v['activity_goods_id'];
        }
//        $goodsUserInfo = $this->getNicknameByActivityId($activity_goods_id)['data'];
        foreach ($list as $k => $v) {
            $list[$k]['goods_user_info'] = [];
//            $list[$k]['goods_user_info'] = $goodsUserInfo[$v['activity_goods_id']] ?? [];
        }
        if ($list) {
            return ['code' => ErrorCode::SUCCESS, 'data' => $list];
        }
        return ['code' => ErrorCode::NOT_EXIST];
    }

    /**
     * 接龙商品详情
     * @param $where
     * @param string[] $field
     * @return array
     */
    public function getGoodsByActivityId(array $where, array $field = ['*'])
    {

        $goodsByActivity = ActivityModel::query()
            ->join('store_activity_goods as ag', 'store_activity.activityID', '=', 'ag.activityID')
            ->join('store_goods as g', 'ag.goods_id', '=', 'g.id')
            ->where(['store_activity.activityID' => $where['activityID']])->select($field)->first()->toArray();
        $goodsByActivity['activity_prams'] = json_decode($goodsByActivity['activity_prams'], true);
        $goodsByActivity['spell_num'] = $goodsByActivity['activity_prams']['number'] + $goodsByActivity['spell_num'];
        $goodsByActivity['begin_date'] = strtotime($goodsByActivity['begin_date']);
        $goodsByActivity['end_date'] = strtotime($goodsByActivity['end_date']);
        $goodsByActivity['goods_user_info'] = array_values($this->getNicknameByActivityId([$goodsByActivity['activity_goods_id']])['data'])[0]??[];
        return $goodsByActivity;
    }

    /**
     *
     * 购物车秒杀商品
     * @param array $goods_id
     * @return array
     * @author lulongfei
     */
    public static function  getSeckillGoodsList(array $activity_goods_id,int $shop_id)
    {
        $field = [
            'a.activityID',
            'a.activityType',
            'ag.goods_title as title',
            'a.begin_date',
            'a.end_date',
            'a.shop_ids',
            'a.activity_prams',
            'a.status',
            'a.is_deleted',
            'a.image',
            'g.logo',
            'ag.goods_id',
            'ag.goods_spec',
            'ag.stock',
            'ag.per_can_buy_num',
            'ag.commission',
            'ag.group_id',
            'ag.costprice as price_market',
            'ag.price_selling',
            'ag.current_cost_price as cost_price',
            'ag.id as activity_goods_id'
        ];
        $date = date('Y-m-d H:i:s');
       $goodsInfo = ActivityModel::query()
           ->from('store_activity as a')
            ->join('store_activity_goods as ag', 'a.activityID', '=', 'ag.activityID')
           ->join('store_goods as g','ag.goods_id','=','g.id')
           ->whereRaw('FIND_IN_SET(?,a.shop_ids)', [$shop_id])
           ->select($field)
           ->whereIn('a.status',[2,3])
           ->where(['a.is_deleted'=>0])
           ->whereRaw("begin_date <= '$date'")
            ->whereIn('ag.id',$activity_goods_id)
            ->get()->toArray();
       return $goodsInfo;
    }
    /**
     *
     * 购物车限购商品  及时达
     * @param array $goods_id
     * @return array
     */
    public static function  getQuotaGoodsList(array $activity_goods_id,int $shop_id)
    {
        $field = [
            'a.activityID',
            'a.activityType',
            'ag.goods_title as title',
            'a.begin_date',
            'a.end_date',
            'a.shop_ids',
            'a.activity_prams',
            'a.status',
            'a.is_deleted',
            'a.image',
            'g.logo',
            'ag.goods_id',
            'ag.goods_spec',
            'ag.stock',
            'ag.per_can_buy_num',
            'ag.commission',
            'ag.group_id',
            'ag.costprice as price_market',
            'ag.price_selling',
            'ag.current_cost_price as cost_price',
            'ag.id as activity_goods_id'
        ];
        $date = date('Y-m-d H:i:s');
        $goodsInfo = ActivityModel::query()
            ->from('store_activity as a')
            ->join('store_activity_goods as ag', 'a.activityID', '=', 'ag.activityID')
            ->join('store_goods as g','ag.goods_id','=','g.id')
            ->whereRaw('FIND_IN_SET(?,a.shop_ids)', [$shop_id])
            ->select($field)
            ->whereIn('a.status',[1])
            ->where(['a.is_deleted'=>0])
            ->whereRaw("a.begin_date <= '$date' and a.end_date > '$date'")
            ->whereIn('ag.id',$activity_goods_id)
            ->get()->toArray();
        return $goodsInfo;
    }
    /**
     * 获取商品下单用户信息
     * @param $where
     * @return array
     */
    public function getNicknameByActivityId(array $where)
    {
        $redis = $this->container->get(RedisFactory::class)->get('resource');
        $generator = $this->container->get(Generator::class);
        if($redis->exists('follow:name100')){
            $name = json_decode($redis->get('follow:name100'), true);
        }else{
            $name = $generator->name100();
            $redis->set('follow:name100',json_encode($name));
        }
        if($redis->exists('follow:faceUrl')){
            $faceUrl = json_decode($redis->get('follow:faceUrl'), true);
        }else{
            $faceUrl = $generator->randAvatarOss();
            $redis->set('follow:faceUrl',json_encode($faceUrl));
        }
        Carbon::setLocale('zh');
        $orderUser =$this->container->get(OrderMiniService::class)->getFollowGoodsInfo($where);

        $userName = [];
        if (empty($orderUser)) {
            foreach ($where as $v) {
                $userName[$v] = $this->userByOrder($v, 0, $name, $faceUrl, $userName, $redis);
            }
        } else {
            $userBrActivityGoodsId = [];
            $userOrderTime = [];
            $goodsNum = [];
            foreach ($orderUser as $item) {
                if (isset($userBrActivityGoodsId[$item['activity_goods_id']]) && in_array($item['mid'], $userBrActivityGoodsId[$item['activity_goods_id']])) {
                    continue;
                }
                $userBrActivityGoodsId[$item['activity_goods_id']][] = $item['mid'];
                $userOrderTime[$item['activity_goods_id']][$item['mid']] = (int)strtotime($item['create_at']);
                $goodsNum[$item['activity_goods_id']][$item['mid']] = $item['number'];

            }
            foreach ($where as $k1) {
                if (!$userBrActivityGoodsId[$k1]) {
                    $userBrActivityGoodsId[$k1] = [];
                }
            }
            $mid = [];
            array_walk_recursive($userBrActivityGoodsId, function ($value) use (&$mid) {
                if ($value) {
                    array_push($mid, $value);
                }

            });
            $_userName = $this->container->get(MemberService::class)->moreMemberInfoByIds($mid,['nickname', 'face_url', 'mid']);

            foreach ($userBrActivityGoodsId as $key => $vo) {
                foreach ($_userName as $k => $v) {

                    if (in_array($v['mid'], $vo)) {
                        $userName[$key][] = [
                            'nickname' => $v['nickname'],
                            'face_url' => $v['face_url'],
                            'time' => $userOrderTime[$key][$v['mid']],
                            'num' => $goodsNum[$key][$v['mid']]
                        ];
                    } else {
                        $userName[$key] = [];
                    }
                }

            }
            foreach ($userName as $k2 => $value) {
                $num = count($value);
                $userName[$k2] = $this->userByOrder($k2, $num, $name, $faceUrl, $userName, $redis);
            }
        }
        return ['code' => ErrorCode::SUCCESS, 'data' => $userName];


    }

    /**
     * 拼接用户信息
     * @param int $key
     * @param int $num
     * @param array $name
     * @param array $faceUrl
     * @param array $userName
     * @param object $redis
     * @return array|mixed
     */
    public function userByOrder(int $key, int $num, array $name, array $faceUrl, array $userName, object $redis)
    {
        if ($user = $redis->hGet('follow:userByorder', 'activity_goods_id' . $key)) {
            $userName[$key] = json_decode($user, true);
            return $userName[$key];
        }
        if ($num < 5) {
            $randNameKey = array_rand($name, 5 - $num);
            $randFaceKey = array_rand($faceUrl, 5 - $num);
            for ($i = 0; $i < 5 - $num; $i++) {
                $userName[$key][] = ['nickname' => $name[$randNameKey[$i]], 'face_url' => $faceUrl[$randFaceKey[$i]], 'time' => time() - mt_rand(1, 100) * 60, 'num' => mt_rand(1, 3)];
            }
        }
        if ($num > 5) {
            $userName[$key] = array_slice($userName[$key], 0, 5);
        }
        $userOrderTime = array_column($userName[$key], 'time');
        array_multisort($userOrderTime, SORT_DESC, $userName[$key]);
        foreach ($userName[$key] as &$v2) {
            $v2['time'] = Carbon::parse($v2['time'])->diffForHumans();
        }
        $redis->hSet('follow:userByorder', 'activity_goods_id' . $key, json_encode($userName[$key]));
        $redis->expire('follow:userByorder', 60);
        return $userName[$key];
    }

    /**
     * 获取活动信息
     *
     * @param $activity_goods_id
     * @param array $field
     * @return array|bool[]
     */
    public function GetActivityInfoByActivityId($activity_goods_id, $field = ['*'])
    {
        $StoreActivityGoods = ActivityGoodsModel::query()->where(['id' => $activity_goods_id])->select($field)->first();
        if (empty($StoreActivityGoods)) {
            $activityGoodsInfo = [
                'is_lost' => true,
            ];
        } else {
            $StoreActivityGoods = $StoreActivityGoods->toArray();
            $StoreActivity = ActivityModel::query()
                ->where(['activityID' => $StoreActivityGoods['activityID']])
                ->select('activityType', 'end_date', 'begin_date', 'activity_prams')
                ->first();
            if (!$StoreActivity) {
                return [];
            }
            $StoreActivity = $StoreActivity->toArray();
            $activity_prams_arr = json_decode($StoreActivity['activity_prams'], true);
            $StoreGoodsArr = GoodsModel::query()
                ->where(['id' => $StoreActivityGoods['goods_id']])
                ->select('cate_id', 'goods_crm_code', 'wx_crm_code','scant_id', 'ratio', 'image', 'content')
                ->first();
            if (!$StoreGoodsArr) {
                return [];
            }
            $StoreGoodsArr = $StoreGoodsArr->toArray();
            $ActivityTimeArr = self::getGroupActivityTime($StoreActivity, $StoreActivity['activityType']);
            $goodsInfoData = array_merge($StoreActivityGoods, $StoreActivity, $activity_prams_arr, $StoreGoodsArr, $ActivityTimeArr);
            $TimeArr = self::getGroupActivityTime($StoreActivity, $StoreActivity['activityType']);
            $goodsInfoData['remaining_time'] = $TimeArr['remaining_time'];
            $goodsInfoData['remaining_type'] = $TimeArr['remaining_type'];
            $goodsInfoData['number'] = !empty($goodsInfoData['number']) ? $goodsInfoData['number'] : 0;
            $goodsInfoData['share_img'] = !empty($goodsInfoData['share_img']) ? $goodsInfoData['share_img'] : '';
            $activityGoodsInfo = [
                'activityID' => $goodsInfoData['activityID'],
                'activityType' => $goodsInfoData['activityType'],
                'activity_goods_id' => $goodsInfoData['activity_goods_id'],
                'group_num' => $goodsInfoData['group_num'] ?? 0,
                'spell_num' => $goodsInfoData['spell_num'] + $goodsInfoData['number'],
                'pick_time' => $goodsInfoData['pick_time'] ?? '',
                'end_date' => $goodsInfoData['end_date'],
                'cate_id' => $goodsInfoData['cate_id'],
                'goods_id' => $goodsInfoData['goods_id'],
                'scant_id' => $goodsInfoData['scant_id'],
                'ratio' => $goodsInfoData['ratio'],
                'itemCode' => $goodsInfoData['wx_crm_code'],
                'group_id' => $goodsInfoData['group_id'],
                'title' => $goodsInfoData['goods_title'],
                'goods_spec' => $goodsInfoData['goods_spec'],
                'price_selling' => $goodsInfoData['price_selling'],
                'share_img' => $goodsInfoData['share_img'],
                'image' => explode('|', $goodsInfoData['image']),
                'content' => $goodsInfoData['content'],
                'price_market' => $goodsInfoData['costprice'],
                'number_stock' => $goodsInfoData['stock'],    // 限购读库存
                'salesQuantity' => $goodsInfoData['salesQuantity'] ?? 0,
                'remaining_time' => $goodsInfoData['remaining_time'],
                'remaining_type' => $goodsInfoData['remaining_type'],

            ];
        }
        return $activityGoodsInfo;
    }

    /**
     * 获取活动时间
     *
     * @param $StoreActivity
     * @param $activityType
     * @return array
     */
    public static function getGroupActivityTime($StoreActivity, $activityType)
    {
        $remaining_time = '';
        $remaining_type = '';
        $now_time = time();
        if ($activityType == 0) {
            $end_date = $StoreActivity['end_date'];
            if (strtotime($end_date) > $now_time) {
                $remaining_type = 1;
                $remaining_time = strtotime($end_date) - $now_time;
            } else {
                $remaining_type = 2;
            }
        }
        if ($activityType == 1) {
            $end_date = $StoreActivity['end_date'];
            $begin_date = $StoreActivity['begin_date'];
            if ($now_time > strtotime($begin_date) && $now_time < strtotime($end_date)) {
                $remaining_type = 1;
                $remaining_time = strtotime($end_date) - $now_time;
            }
            if ($now_time >= strtotime($end_date)) {
                $remaining_type = 2;
                $remaining_time = $now_time - strtotime($end_date);
            }
            if ($now_time <= strtotime($begin_date)) {
                $remaining_type = 3;
                $remaining_time = strtotime($begin_date) - $now_time;
            }
        }
        $data = [
            'remaining_type' => $remaining_type,
            'remaining_time' => $remaining_time,
        ];
        return $data;
    }

    /**
     * 获取限时抢购的列表
     *
     * @param $shop_id
     * @param array $where
     * @return Builder[]|\Hyperf\Database\Model\Collection
     */
    public function getSaleFlashList($shop_id, $where = [])
    {
        $filed = ['activityID', 'activityType', 'begin_date', 'end_date', 'activity_prams',];
        $flashSaleList = ActivityModel::query()
            ->where('status', 1)
            ->where('activityType', 1)
            ->select($filed)
            ->where($where)
            ->whereRaw('FIND_IN_SET(' . $shop_id . ',shop_ids)')
            ->orderByRaw('begin_date asc')
            ->get();
        foreach ($flashSaleList as $flashSale) {
            $timer = ActivityModel::getSaleActivityTime($flashSale['begin_date'], $flashSale['end_date']);
            $flashSale->sale_type = $timer['sale_type'];
        }
        return $flashSaleList;
    }

    /**
     * 活动详细数据
     *
     * @param $activity_id
     * @param $shop_id
     * @return array
     */
    public function detailInfo($activity_id, $shop_id)
    {
        $field = [
            'activityID',
            'activityType',
            'begin_date',
            'end_date',
            'activity_prams',
        ];
        $SaleActivityArr = ActivityModel::query()->where(['activityID' => $activity_id])->select($field)->first();
        $activity_prams_arr = json_decode($SaleActivityArr['activity_prams'], true);
        $saleActivityTypeArr = ActivityModel::getSaleActivityTime($SaleActivityArr['begin_date'], $SaleActivityArr['end_date']);
        $goods_list = $this->getSaleActivityGoods($activity_id, $shop_id);
        return [
            'begin_date' => $SaleActivityArr['begin_date'],
            'end_date' => $SaleActivityArr['end_date'],
            'panic_num' => $activity_prams_arr['number'],
            'saleTypeArr' => $saleActivityTypeArr,
            'goodsList' => $goods_list,
        ];
    }

    /**
     * 获取限时抢购活动商品列表
     *
     * @param $activityID
     * @param $shop_id
     * @return array
     */
    public function getSaleActivityGoods($activityID, $shop_id)
    {
        $goodsArr = ActivityGoodsModel::query()
            ->where('activityID', $activityID)
            ->select('id as activity_goods_id', 'activityID', 'goods_id', 'goods_title', 'group_id', 'goods_logo', 'goods_spec', 'costprice', 'spell_num', 'price_selling', 'stock')
            ->get();
        if (!$goodsArr) {
            return [];
        }
        $goodsArr = $goodsArr->toArray();
        $goodsIdsArr = array_column($goodsArr, 'goods_id');
        $ActivityGoodsArr = GoodsModel::query()
            ->whereIn('id', $goodsIdsArr)
            ->select('id', 'goods_crm_code', 'scant_id', 'ratio', 'status', 'is_deleted')
            ->get()
            ->toArray();
        $GoodsIdsArr = array_column($ActivityGoodsArr, null, 'id');
        $groupData = [];
        foreach ($goodsArr as $v) {
            $groupData [] = array_merge($v, $GoodsIdsArr[$v['goods_id']]);
        }
        $SaleActivityGoodsData = [];
        foreach ($groupData as $value) {
            if ($value['status'] == 1 && $value['is_deleted'] == 0) {
                $SaleActivityGoodsData[] = [
                    'activity_goods_id' => $value['activity_goods_id'],
                    'group_id' => $value['group_id'],
                    'itemCode' => 0,
                    'scant_id' => $value['scant_id'],
                    'ratio' => $value['ratio'],
                    'spell_num' => $value['spell_num'],
                    'goods_id' => $value['goods_id'],
                    'title' => $value['goods_title'],
                    'logo' => $value['goods_logo'],
                    'goods_spec' => $value['goods_spec'],
                    'price_selling' => $value['price_selling'],
                    'price_market' => $value['costprice'],
                    'number_stock' => 0,
                    'salesQuantity' => 0,
                    'stock' => $value['stock'],
                ];
            }
        }
        return $SaleActivityGoodsData;
    }

    /**
     * 秒杀缓存数据，如果有
     * @param string $shopId
     * @return array|string
     * @author liule
     */
    public function getSecKillCache(string $shopId){
        /**
         * redis 缓存设计缺陷 暂时屏蔽
         */
//        $key = 'seckill:' . date('Ymd');
//        $list = $this->seckillRedis->hGet($key, $shopId);
//        $list = $list ? json_decode($list, true) : false;
//        // redis挂掉的 paly B
//        if (!$list) $list = $this->seckillActAll($shopId);
        return $this->seckillActAll($shopId);
    }

    /**
     * 小程序首页秒杀
     * 秒杀总场次
     * 当前场次，距离结束时间
     * 当前场次秒杀商品列表（主图，标题，秒杀价，秒杀库存 ，普通库存，每人限购数量，购物车数量）
     *
     * @param string $shopId
     * @param int $mid
     * @return array
     * @author liule
     */
    public function getMiniAppHomeSecKill(string $shopId, int $mid)
    {
        $list = $this->getSecKillCache($shopId);
        $listMap = [];
        foreach ($list as $k => $v){
            foreach ($v['activity_goods'] as $kk => $vv){
                $listMap[] = array_merge($vv, [
                    'activity_prams' => $v['activity_prams'],
                    'begin_date' => $v['begin_date'],
                    'end_date' => $v['end_date'],
                    'activity_id' => $v['activityID']
                ]);
            }
        }
        if (empty($listMap)) return ['code' => 1,'msg' => '店铺今天暂无秒杀活动','data' => null];
        unset($list);
        // 总场次
        $total_sessions = count(array_unique(array_column($listMap, 'activity_id')));
        // 所有场次的数据
        $map = array_column($listMap, null, 'activity_id');
        //所有场次的时间段数据
        $timeMap = array_column($map,'activity_prams','activity_id');
        $actIdEndStamp = [];
        foreach ($timeMap as $k => $v){
            $endStamp = strtotime(date('Y-m-d ' . json_decode($v, true)['end_time']));
            $actIdEndStamp[$endStamp] = $k;
        }
        $lastActId = $actIdEndStamp[max(array_keys($actIdEndStamp))]; // 最后时间段的场次活动
        $startTimeMap = []; // 进行中的活动
        $endTimeMap = [];   //结束的活动
        foreach ($timeMap as $k => $v){
            $start = json_decode($v, true)['start_time'];
            $end = json_decode($v, true)['end_time'];
            $sd = strtotime(date('Y-m-d ' . $start)) - time();
            $ed = strtotime(date('Y-m-d ' . $end)) - time();
            $endTimeMap[$ed]['activity_id'] = $k;
            $endTimeMap[$ed]['start_time'] = $start;
            $endTimeMap[$ed]['end_time'] = $end;
            $endTimeMap[$ed]['diff'] = $ed;
            if ($sd < 0) continue;
            $startTimeMap[$sd]['activity_id'] = $k;
            $startTimeMap[$sd]['start_time'] = $start;
            $startTimeMap[$sd]['end_time'] = $end;
            $startTimeMap[$sd]['diff'] = $sd;
        }
        $currActId = 0;
        // 即将开始的秒杀
        if (!empty($startTimeMap)){
            $min = min(array_column($startTimeMap, 'diff'));
            //0 即将开始，1 秒杀中
            list($currActId, $start_time, $end_time, $activity_status) = [
                $startTimeMap[$min]['activity_id'],
                $startTimeMap[$min]['start_time'],
                $startTimeMap[$min]['end_time'],
                0
            ];
        }
        foreach ($map as $k => $v){
            // 秒杀中
            $time = json_decode($v['activity_prams'], true);
            if ( strtotime(date("Y-m-d {$time['start_time']}")) < time() and  time() < strtotime(date("Y-m-d {$time['end_time']}")) ){
                //0 即将开始，1 秒杀中
                list($currActId, $start_time, $end_time, $activity_status) = [
                    $v['activity_id'],
                    $time['start_time'],
                    $time['end_time'],
                    1
                ];
                break;
            }
        }
        // 没有即将开始的活动或秒杀中的活动 返回最后一个时间段的活动
        if ($currActId == 0){
            $max = max(array_column($endTimeMap, 'diff'));
            //0 即将开始，1 秒杀中 2 已结束
            list($currActId, $start_time, $end_time, $activity_status) = [
                $endTimeMap[$max]['activity_id'],
                $endTimeMap[$max]['start_time'],
                $endTimeMap[$max]['end_time'],
                2
            ];
        }
        $currList = array_filter($listMap, fn($v) => $v['activity_id'] == $currActId);
        $info = $this->cartAndRemindInfo($mid, array_column($currList, 'activity_goods_id'));
        $paidSecKillGoods = $this->container->get(OrderService::class)->getPaidSecKillGoodsNum($mid, array_column($currList, 'activity_goods_id'));
        $currList = array_map(fn($v) => $v + [
                'paid_num' => $paidSecKillGoods[$v['activity_goods_id']] ?? 0,
                'cart_goods_num' => $info['cartInfo'][$v['activity_goods_id']] ?? 0,
                'remind_status' => $info['remind'][$v['activity_goods_id']] ?? 0,
                'seckill_total_stock' => $v['stock'] + $v['spell_num'] - $v['spell_num_default']
            ], $currList);
        return [
            'code' => 1,
            'msg' => '获取首页秒杀数据成功',
            'data' => [
                'is_login' => $mid == 0 ? 0 : 1,
                'total_sessions' => $total_sessions,
                'start_time' => $start_time ?? '',
                'end_time' => $end_time ?? '',
                'activity_status' => $activity_status ?? '',
                'is_last_activity' => $currActId ==  $lastActId ? 1 : 0,
                'list' => array_values($currList)
            ]
        ];
    }

    /**
     * 全部秒杀场次
     * @param string $shopId
     * @param int $mid
     * @return array
     * @author liule
     */
    public function getMiniAppSecKillAll(string $shopId, int $mid){
        $list = $this->getSecKillCache($shopId);
        if (empty($list)) return ['code' => 1,'msg' => '店铺今天暂无秒杀活动','data' => null];
        $actGoodsIds = [];
        foreach ($list as $k => $v){
            $actGoodsIds = array_merge(array_column($v['activity_goods'], 'activity_goods_id'), $actGoodsIds);
        }
        $paidSecKillGoods = $this->container->get(OrderService::class)->getPaidSecKillGoodsNum($mid, $actGoodsIds);
        $info = $this->cartAndRemindInfo($mid, $actGoodsIds);
        foreach ($list as $k => $v){
            $time = json_decode($v['activity_prams'], true);
            $list[$k]['activity_id'] = $v['activityID'];
            $list[$k]['start_time'] = $time['start_time'];
            $list[$k]['end_time'] = $time['end_time'];
            $start_time_ms = strtotime(date("Y-m-d {$time['start_time']}"));
            $end_time_ms = strtotime(date("Y-m-d {$time['end_time']}"));
            if (time() < $start_time_ms) $list[$k]['activity_status'] = 0; // 即将开抢
            if (time() > $end_time_ms) $list[$k]['activity_status'] = 2; // 已结束、已开抢
            if ( $start_time_ms < time() and  time() < $end_time_ms) $list[$k]['activity_status'] = 1; // 疯抢中
            foreach ($v['activity_goods'] as $kk => $vv){
                $list[$k]['activity_goods'][$kk]['activity_id'] = $vv['activityID'];
                $list[$k]['activity_goods'][$kk]['paid_num'] = $paidSecKillGoods[$vv['activity_goods_id']] ?? 0;
                $list[$k]['activity_goods'][$kk]['cart_goods_num'] = $info['cartInfo'][$vv['activity_goods_id']] ?? 0;
                $list[$k]['activity_goods'][$kk]['remind_status'] = $info['remind'][$vv['activity_goods_id']] ?? 0;
                $list[$k]['activity_goods'][$kk]['seckill_total_stock'] = $vv['stock'] + $vv['spell_num'] - $vv['spell_num_default'];
                unset($list[$k]['activity_goods'][$kk]['activityID']);
            }
            unset($list[$k]['activityID']);
        }
        $column = array_column($list, 'start_time');
        array_multisort($column, SORT_ASC, $list);
        $pic = $this->container->get(Generator::class)->randAvatarOss(50);
        // 购买人数
        $paidCount = OrderModel::query()
            ->where(['order_source' => Stakeholder::NEXT_DAY_ORDER, 'order_type' => Stakeholder::ORDER_TYPE_SECKILL])
            ->whereBetween('pay_at', [Carbon::now()->startOfDay(), Carbon::now()->endOfDay()])
            ->distinct()
            ->pluck('mid');
        return [
            'code' => 1,
            'msg' => '获取全部秒杀数据成功',
            'data' => [
                'is_login' => $mid == 0 ? 0 : 1,
                'rob' => 1231 + collect($paidCount)->count() * 10,
                'pic' => $pic,
                'list' => $list
            ]
        ];
    }

    /**
     * 秒杀 购物车数量，提醒状态
     * @param int $mid
     * @param array $actGoodsIds
     * @return array
     * @author liule
     */
    public function cartAndRemindInfo(int $mid, array $actGoodsIds){
        $cartInfo = HfCartModel::query()
            ->where(['mid' => $mid])
            ->whereIn('activity_goods_id', $actGoodsIds)
            ->pluck('goods_num', 'activity_goods_id');
        $remind = SecKillActivityRemindModel::query()
            ->selectRaw('IF(id, 1, 0) as remind_status,activity_goods_id')
            ->where(['mid' => $mid])
            ->whereIn('activity_goods_id', $actGoodsIds)
            ->pluck('remind_status', 'activity_goods_id');
        return ['cartInfo' => $cartInfo, 'remind' => $remind];
    }

    /**
     * 当前店铺当天启用和进行中中的秒杀活动
     * @param string $shopId
     * @return array
     * @author liule
     */
    public function SecKillActAll(string $shopId){
        $filed = [
            'ac.activityID',
            'ac.activity_prams',
            'ac.begin_date',
            'ac.end_date'
        ];
        $ag = 'store_activity_goods';
        $goodsFiled = [
            "{$ag}.id as activity_goods_id",
            "{$ag}.activityID",
            "{$ag}.goods_id",
            "{$ag}.group_id",
            "{$ag}.goods_logo",
            "{$ag}.goods_title",
            "{$ag}.price_selling",
            "{$ag}.stock",
            "{$ag}.spell_num",
            "default({$ag}.spell_num) as spell_num_default",
            "{$ag}.per_can_buy_num",
            "gl.number_stock"
        ];
        $today = [Carbon::now()->startOfDay(), Carbon::now()->endOfDay()];
        return ActivityModel::query()
            ->from('store_activity as ac')
            ->with(['activityGoods' => function($query) use ($ag, $goodsFiled) {
                $query->selectRaw(implode(',', $goodsFiled))
                    ->leftJoin('store_goods_list as gl', "{$ag}.goods_id", '=', 'gl.goods_id');
//                    ->leftJoin('hf_seckill_activity_remind as sar', function ($join) use ($ag, $mid) {
//                        $join->on("{$ag}.id", '=', 'sar.activity_goods_id')->where('sar.mid', $mid);
//                    })
//                    ->leftJoin('hf_cart as cart', function ($join) use ($ag, $mid) {
//                        $join->on("{$ag}.id", '=', 'cart.activity_goods_id')->where('cart.mid', $mid);
//                    });
            }])
            ->select($filed)
            ->whereBetween('ac.begin_date', $today)
            ->where(['ac.activityType' => Stakeholder::ACTIVITY_SECKILL, 'ac.is_deleted' => Stakeholder::ACTIVITY_NON_DELETED])
            ->where('ac.status', '!=', 0)
            ->where('shop_ids', 'regexp', $shopId)
            ->oldest('activityID')
            ->get()
            ->toArray();
    }

    /**
     * 活动商品点击数
     * @param int $activity_goods_id
     * @author liule
     */
    public function secKillGoodsClickNumRecord(int $activity_goods_id){
        parallel([
            function () use ($activity_goods_id){
                ActivityGoodsModel::query()->where('id', $activity_goods_id)->increment('click_num');
            }
        ]);
    }

    /**
     * 修改秒杀活动状态
     * @param int $id
     * @return array
     */
    public function seckillStatus(int $id)
    {
        $operationResults = '';
        $info = ActivityModel::query() -> where('activityID', $id) -> where('activityType', '4') -> first(['status', 'begin_date', 'activity_prams','shop_ids','activityType']);
        if(!$info){
            return ['code' => ErrorCode::NOT_EXIST];
        }
        if (in_array($info -> status, [1, 2])) {
            $statusValue = 0;
            $operationResults = '禁用';
        } else {
            //获取该时间
            $startAndEndTime = json_decode($info -> activity_prams, true);
            $day = trim(substr($info -> begin_date, 0, 10));
            $start = $startAndEndTime['start_time'];
            $end = $startAndEndTime['end_time'];

            // 判断当前秒杀活动的时间是否和其他未禁用秒杀活动时间重叠--相对于店铺而言（因为活动禁用期间，可创建和禁用活动重叠时间的活动）
            // 该活动的开始时间和结束时间
            $startTime = $day.' '.$start;
            $endTime = $day.' '.$end;

            // 筛选出活动日期相同的秒杀活动，不包含已禁用的活动
            $activityList = ActivityModel::query()
                ->select(['activityID', 'shop_ids', 'activity_prams'])
                ->where('is_deleted', 0)
                ->where('activityType', 4)
                ->whereIn('status', [1,2])
                ->whereRaw('INSTR(begin_date, ?) > 0', [$day])
                ->get()->toArray();

            // 拼接活动的时间，并检测时间是否重合
            foreach ($activityList as $key => $val){
                $actTime = json_decode($val['activity_prams'], true);
                $activityList[$key]['act_start_time'] = $day.' '.$actTime['start_time'];
                $activityList[$key]['act_end_time'] = $day.' '.$actTime['end_time'];
                $isTimeCross = $this -> shopGroupService -> isTimeCross($startTime, $endTime, $activityList[$key]['act_start_time'], $activityList[$key]['act_end_time']);
                // 时间冲突
                if($isTimeCross){
                    // 判断两个活动有无相同的店铺，若有则不能变更状态
                    $firstArr = explode(',', $info -> shop_ids);
                    $secondArr = explode(',', $val['shop_ids']);
                    $checkSameShopId = array_intersect($firstArr, $secondArr);
                    if(!empty($checkSameShopId)){
                        return ['code' => ErrorCode::SAME_TIME_AND_SHOP];
                    }
                }
            }

            // 判断当前时间是否处于活动时间中，若是改为进行中，否则改为未生效
            $checkTime = $this -> checkTime($day, $start, $end);
            if($checkTime){
                $statusValue = 2;   // 活动进行中
            }else{
                $statusValue = 1;
                $operationResults = '启用';
            }
        }
        $res = ActivityModel::query()->where('activityID', $id)->where('activityType', '4')->update(['status' => $statusValue]);
        if($res){
            return ['code' => ErrorCode::SUCCESS, 'data' => ['type' => $info->activityType], 'info' => ['target_id' => $id, 'operation_results' => $operationResults]];
        }

        return ['code' => ErrorCode::SERVER_ERROR];
    }

    /**
     * 修改秒杀活动状态
     * @param int $id
     * @return array
     */
    public function freeStatus(int $id): array
    {
        $operationResults = '';
        $info = ActivityModel::query() -> where('activityID', $id) -> where('activityType', '5') -> first(['status', 'begin_date', 'end_date', 'shop_ids', 'activityType']);
        if(!$info){
            return ['code' => ErrorCode::NOT_EXIST];
        }
        if (in_array($info -> status, [1, 2])) {
            $statusValue = 0;
            $operationResults = '禁用';
        } else {
            // 判断当前活动的时间是否和其他未禁用秒杀活动时间重叠--相对于店铺而言（因为活动禁用期间，可创建和禁用活动重叠时间的活动）
            // 该活动的开始时间和结束时间
            $startTime = $info -> begin_date;
            $endTime = $info -> end_date;

            // 筛选出活动日期相同的秒杀活动，不包含已禁用的活动
            $activityList = ActivityModel::query()
                ->select(['activityID', 'shop_ids', 'begin_date', 'end_date'])
                ->where('is_deleted', 0)
                ->where('activityType', 5)
                ->whereIn('status', [1,2])
                ->get();

            if(!$activityList -> isEmpty()){
                // 拼接活动的时间，并检测时间是否重合
                foreach ($activityList as $key => $val){
                    $isTimeCross = $this -> shopGroupService -> isTimeCross($startTime, $endTime, $val['begin_date'], $val['end_date']);
                    // 时间冲突
                    if($isTimeCross){
                        // 判断两个活动有无相同的店铺，若有则不能变更状态
                        $firstArr = explode(',', $info -> shop_ids);
                        $secondArr = explode(',', $val['shop_ids']);
                        $checkSameShopId = array_intersect($firstArr, $secondArr);
                        if(!empty($checkSameShopId)){
                            return ['code' => ErrorCode::SAME_TIME_AND_SHOP];
                        }
                    }
                }
            }

            // 判断当前时间是否处于活动时间中，若是改为进行中，否则改为未生效
            $checkTime = $this -> checkTime('', $startTime, $endTime);
            if($checkTime){
                $statusValue = 2;   // 活动进行中
            }else{
                $statusValue = 1;
                $operationResults = '启用';
            }
        }
        $res = ActivityModel::query()->where('activityID', $id)->where('activityType', '5')->update(['status' => $statusValue]);
        if($res){
            return ['code' => ErrorCode::SUCCESS, 'data' => ['type' => $info->activityType], 'info' => ['target_id' => $id, 'operation_results' => $operationResults]];
        }

        return ['code' => ErrorCode::SERVER_ERROR];
    }

    /**
     * 判断当前时间是否处于给定的时间段中
     * @param $day
     * @param $start
     * @param $end
     *
     * @return bool     处于 true，不处于 false
     */
    public function checkTime($day = '', $start = '', $end = '')
    {
        $timeBegin = strtotime(trim($day.' '.$start));
        $timeEnd = strtotime(trim($day.' '.$end));
        $currentTime = time();

        if($currentTime >= $timeBegin && $currentTime <= $timeEnd){
            return true;
        }
        return false;
    }

    /**
     * 秒杀活动新增店铺
     * @param int $id
     * @param string $shopIds
     * @return array
     */
    public function addShop(int $id, string $shopIds)
    {
//        $res = ActivityModel::query() -> where('activityID', $id)->where('activityType', '4') -> first();
        $res = ActivityModel::query() -> where('activityID', $id) -> first();
        if($res){
            $res->shop_ids = $shopIds;
            $res->save();
//            $actInfo = $this -> getInfoById($id, 4, ['activityID', 'title', 'begin_date', 'activity_prams', 'shop_ids', 'status']);
//            if(!$actInfo['code']){
//                $this->seckillRedis->hSet('seckill', (string)$id, json_encode($actInfo['data']));
//            }
            return ['code' => ErrorCode::SUCCESS, 'data' => ['type' => $res->activityType], 'info' => ['target_id' => $id, 'remarks' => 'shop_ids:'.$shopIds]];
        }else{
            return ['code' => ErrorCode::NOT_IN_FORCE];
        }
    }

    /**
     * 设置开抢提醒
     *
     * User: mengchenchen
     * Created_at: 2020/12/29 0029 15:49
     * Updated_at: 2020/12/29 0029 15:49
     *
     * @param $mid
     * @param $activity_goods_id
     * @param $preempt_time
     * @return array
     */
    public function setSpikeGoodsReminder($mid, $activity_goods_id, $preempt_time)
    {
        $activity_goods = ActivityGoodsModel::query()->find($activity_goods_id);
        if (!$activity_goods) {
            return ['code' => ErrorCode::NOT_IN_FORCE, 'msg' => '不存在的活动商品'];
        }
        $where = ['mid' => $mid, 'activity_goods_id' => $activity_goods_id,];
        $remind = SecKillActivityRemindModel::query()->where($where)->first();
        if ($remind) {
            return ['code' => ErrorCode::NOT_IN_FORCE, 'msg' => '无需重复设置开抢提醒'];
        }
        $data = [
            'mid'               => $mid,
            'activity_goods_id' => $activity_goods_id,
            'activity_id'       => $activity_goods->activityID,
            'preempt_time'      => date('Y-m-d') . ' ' . $preempt_time,
        ];
        $res = SecKillActivityRemindModel::query()->create($data);
        if ($res) {
            $delay = strtotime($res->preempt_time) - time() - 300;
            $delay = $delay > 0 ? $delay : 0;
            $delay += SecKillActivityRemindModel::query()
                ->where('mid', $mid)
                ->where('activity_id', $activity_goods->activityID)
                ->count();
            $this->container->get(DriverFactory::class)->get('subscribe')->push(new SubscribeJob($res->id), $delay);
            $this->secKillGoodsClickNumRecord(intval($activity_goods_id)); // 点击数
            return ['code' => ErrorCode::SUCCESS, 'msg' => '成功'];
        }
        return ['code' => ErrorCode::NOT_IN_FORCE, 'msg' => '失败'];
    }

    /**
     * 设置最近活动开抢提醒
     *
     * User: mengchenchen
     * Created_at: 2020/12/29 0029 15:49
     * Updated_at: 2020/12/29 0029 15:49
     *
     * @param $shop_id
     * @param $mid
     * @return array
     */
    public function setSpikeClosestActivityReminder($shop_id, $mid)
    {
        // 最近即将开始的活动
        $activitys = ActivityModel::query()
            ->select('activityID', 'begin_date', 'activity_prams')
            ->where('activityType', 4)
            ->where('status', 1)
            ->whereRaw("date(`begin_date`) >= '" . date('Y-m-d') . "'")
            ->when($shop_id, function ($q, $shop_id) {
                $q->whereRaw('FIND_IN_SET(' . $shop_id . ',shop_ids)');
            })
            ->orderBy('begin_date', 'asc')
            ->orderBy('activity_prams', 'asc')
            ->get();
        $activity = null;
        foreach ($activitys as $v) {
            $prams = json_decode($v->activity_prams, true);
            if (strtotime(date('Y-m-d ') . $prams['start_time']) > time()) {
                $activity = $v;
                break;
            }
        }
        if (!$activity) {
            return ['code' => ErrorCode::NOT_EXIST, 'msg' => '没有即将开始的活动'];
        }
        $remind = SecKillActivityRemindModel::query()->where('is_closest', 1)->where('mid', $mid)->first();
        if (!$remind) {
            $activity_prams = json_decode($activity->activity_prams, true);
            $preempt_time = date('Y-m-d H:i:s', (strtotime(date('Y-m-d ') . $activity_prams['start_time']) - 300));
            $remind = SecKillActivityRemindModel::query()->create([
                'is_closest'   => 1,
                'mid'          => $mid,
                'preempt_time' => $preempt_time,
            ]);
        }
        $remind->activity_id = $activity->activityID;
        $remind->save();
        $delay = strtotime($remind->preempt_time) - time() - 300;
        $delay = $delay > 0 ? $delay : 0;
        $this->container->get(DriverFactory::class)->get('subscribe')->push(new SubscribeJob($remind->id), $delay);
        return ['code' => ErrorCode::SUCCESS, 'msg' => '成功'];
    }

    /**
     * 秒杀销售总额，不包含退款
     * @return \Hyperf\Utils\HigherOrderTapProxy|mixed|void
     */
    public function totalSales($date = null)
    {
        $res = OrderModel::query()
            ->selectRaw("
                SUM(total_price) as total_price
            ")
            -> when($date ?? 0, function ($query, $date) {
                return $query->whereDate('pay_at', '=', $date);
            })
            ->where('is_pay', 1)
            ->where('order_type',9)
            ->first();

        // 去除退款金额
        $refundAmount = $this -> getSeckillRefundAmount($date);

        return bcsub((string)$res -> total_price, (string)$refundAmount, 2);
    }

    /**
     * 秒杀退款总额
     * @return string
     *
     * 说明：客至退款 + 吾享退款
     */
    public function getSeckillRefundAmount($date = null)
    {
        $kzRefund = KzRefundLogModel::query()
            -> selectRaw("
                SUM(refund_balance_money) as refund_money
            ")
            -> when($date ?? 0, function ($query, $date) {
                return $query->whereDate('create_time', '=', $date);
            })
            -> where('order_type', 9)
            -> first();

        $wxRefund = WxRefundLogModel::query()
            -> selectRaw("
                SUM(refund_wx_money) as refund_money
            ")
            -> when($date ?? 0, function ($query, $date) {
                return $query->whereDate('create_time', '=', $date);
            })
            -> where('order_type', 9)
            -> first();

        $totalRefundAmount = bcadd((string)$wxRefund->refund_money, (string)$kzRefund->refund_money, 2);

        return $totalRefundAmount;
    }

    /**
     * 秒杀退款每月总额
     * @param null $date
     * @param $dateType
     * @return array
     * 说明：客至退款 + 吾享退款
     */
    public function getSeckillRefundPerDay($date = null, $dateType): array
    {
        $dateFormat = '';
        switch ($dateType)
        {
            case 'year':
                $dateFormat = '%Y-%m';
                break;
            case 'month':
            case 'week':
            case 'custom':
                $dateFormat = '%Y-%m-%d';
                break;
        }

        // 查出每笔订单的退款
        $kzRefund = KzRefundLogModel::query()
            -> selectRaw("
                DATE_FORMAT(create_time, '".$dateFormat."') as date,
                SUM(refund_balance_money) as refund_money
            ")
            -> when($date ?? 0, function ($query, $date) {
                return $query->whereBetween('create_time', $date);
            })
            -> where('order_type', 9)
            -> groupBy(['date'])
            -> pluck('refund_money', 'date') -> toArray();

        $wxRefund = WxRefundLogModel::query()
            -> selectRaw("
                DATE_FORMAT(create_time, '".$dateFormat."') as date,
                SUM(refund_wx_money) as refund_money
            ")
            -> when($date ?? 0, function ($query, $date) {
                return $query->whereBetween('create_time', $date);
            })
            -> where('order_type', 9)
            -> groupBy(['date'])
            -> pluck('refund_money', 'date') -> toArray();

        $totalRefundAmount = $this -> arrayAdd($kzRefund, $wxRefund);

        return $totalRefundAmount;
    }

    /**
     * 两个数组相加，相同键名，键值相加。键名不同，保留键值
     * @param array $arr1
     * @param array $arr2
     */
    public function arrayAdd(array $arr1 = [],array $arr2 = [])
    {
        $arr3 = $arr1 + $arr2;
        $arr4 = array_intersect_key($arr2, $arr1);
        foreach($arr4 as $k => $v) {
            $arr3[$k] += $v;
        }
        return $arr3;
    }

    /**
     * 秒杀活动举行天数，包含今天，且小于等于今天
     * @return int
     */
    public function cumulativeSalesDays()
    {
        $todayDate = date('Y-m-d', time()); // 2020-12-25
        $res = ActivityModel::query()
            ->selectRaw("
                DATE_FORMAT(begin_date, '%Y-%m-%d') as begin_date
            ")
            ->where('activityType', 4)
            ->whereDate('begin_date', '<=', $todayDate)
            ->groupBy(['begin_date'])
            ->get();

        return count($res);
    }

    /**
     * 秒杀活动商品销售量（不含退款商品）
     * @return string
     */
    public function seckillSalesVolume()
    {
        // 查出所有秒杀活动付过款的商品数量
        $payNum = OrderModel::query()
            ->from('store_order as o')
            ->leftJoin('store_order_goods as og','o.order_no','=','og.order_no')
            ->where('o.is_pay', 1)
            ->where('o.order_type', 9)
            ->sum('og.number');

        // 查出所有秒杀活动的退货商品数量
        $refundNum = OrderModel::query()
            ->from('store_order as o')
            ->leftJoin('store_order_goods as og','o.order_no','=','og.order_no')
            ->selectRaw("
                (SUM(refund_number) + SUM(shop_refund_number)) as refund_amount
            ")
            ->where('og.refund_at', '<>', null)
            ->where('o.is_pay', 1)
            ->where('o.order_type', 9)
            ->value('refund_amount');

        // 付款总量 - 退货量 = 销售总量
        return bcsub((string)$payNum, (string)$refundNum);
    }

    /**
     * 秒杀订单量，包含未退款的和部分退的订单
     * @return string
     */
    public function seckillOrderNum()
    {
        // 未退款订单
        $count1 = OrderModel::query()
            ->selectRaw("COUNT(*) as sale_order")
            ->where('is_pay', 1)
            ->where('order_type', 9)
            ->where('refund_at', null)
            ->value('sale_order');  // 1

        // 部分退款订单
        $count2 = OrderModel::query()
            ->selectRaw("count(*) as sale_order")
            ->where('is_pay', 1)
            ->where('order_type', 9)
            ->where('is_whole', 1)
            ->value('sale_order');

        return bcadd((string)$count1, (string)$count2);
    }

    /**
     * 秒杀销售趋势柱状图
     * @param array $params
     * @return array
     */
    public function salesTrend($params = [])
    {
        if($params['range'] == 'year'){
            // 构造月份列表
            $elevenMonthAgo = substr(Carbon::parse('-11 months')->toDateString(), 0, 7); // 11个月之前    2020-01
            $thisMonth = trim(substr(Carbon::parse()->toDateString(),0, 7)); // 本月   2020-12
            $thisMonthDay = Carbon::parse()->toDateString(); // 本月今天日期    2020-12-25
            $cycleList = $this -> prMonths($elevenMonthAgo, $thisMonth);
            $elevenMonthAgoFirstDay = $elevenMonthAgo.'-01';    // 2020-01-01
            $totalPerCycle = $this ->totalSalesIntervalCalculation($elevenMonthAgoFirstDay, $thisMonthDay, 'year');
        }elseif ($params['range'] == 'month'){
            $beginDate = date('Y-m-01', strtotime(date("Y-m-d")));  // 当月第一天    2020-12-01
            $endDate = date('Y-m-d', strtotime("$beginDate +1 month -1 day"));  // 当月最后一天    2020-12-31
            $cycleList = $this -> prDates($beginDate, $endDate);
            $totalPerCycle = $this ->totalSalesIntervalCalculation($beginDate, $endDate, 'month');
        }elseif ($params['range'] == 'week'){
            $startDayOfWeek = Carbon::now()->startOfWeek()->toDateString();
            $endDayOfWeek = Carbon::now()->endOfWeek()->toDateString();
            $cycleList = $this -> prDates($startDayOfWeek, $endDayOfWeek);
            $totalPerCycle = $this ->totalSalesIntervalCalculation($startDayOfWeek, $endDayOfWeek, 'week');
        }elseif ($params['range'] == 'custom'){
            $startDay =  trim(substr($params['date_range'], 0, 10));    // 2020-12-01
            $endDay =  trim(substr($params['date_range'], -10));    // 2020-12-31
            $cycleList = $this -> prDates($startDay, $endDay);
            $totalPerCycle = $this ->totalSalesIntervalCalculation($startDay, $endDay, 'custom');
        }

        $list = [];
        foreach($cycleList as $key => $val){
            $list[$key]['date'] = $val;
            if(array_key_exists($val, $totalPerCycle)){
                $list[$key]['total_price'] = $totalPerCycle[$val];
            }else{
                $list[$key]['total_price'] = '0.00';
            }
        }

        return ['code' => ErrorCode::SUCCESS, 'data' => $list];
    }

    /**
     * 秒杀销售额区间计算
     * @return \Hyperf\Utils\HigherOrderTapProxy|mixed|void
     *
     * 改进：目前存在的问题，只有有销售额的条目，才能减退款，后期改进。
     */
    public function totalSalesIntervalCalculation($start, $end, $dateType)
    {
        $dateFormat = '';
        switch ($dateType)
        {
            case 'year':
                $dateFormat = '%Y-%m';
                $date = [$start.'-01 00:00:00', $end.' 23:59:59'];
                break;
            case 'month':
            case 'week':
            case 'custom':
                $dateFormat = '%Y-%m-%d';
                $date = [$start.' 00:00:00', $end.' 23:59:59'];
                break;
        }

        $totalPerDay = OrderModel::query()
            ->selectRaw("
                DATE_FORMAT(pay_at, '".$dateFormat."') as date,SUM(total_price) as total_price
            ")
            ->whereBetween('pay_at', [$start.' 00:00:00', $end.' 23:59:59'])
            ->where('is_pay', 1)
            ->where('order_type', 9)
            ->groupBy(['date'])
            ->pluck('total_price','date')->toArray();

        $refundMoneyPerday = $this -> getSeckillRefundPerDay($date, $dateType);
        foreach($totalPerDay as $key => $val){
            if(array_key_exists($key, $refundMoneyPerday)){
                $totalPerDay[$key] = bcsub((string)$totalPerDay[$key], (string)$refundMoneyPerday[$key], 2);
            }
        }

        return $totalPerDay;
    }

    /**
     * @param array $params
     * @return array
     */
    public function seckillStoreSalesRanking($params = [])
    {
        if ($params['range'] == 'year') {
            // 获取本年第一天到今天的日期
            $firstDay = date("Y") . '-01-01'; //   2020-01-01
            $today = date("Y-m-d", time()); //   2020-12-17

        }elseif ($params['range'] == 'month') {
            // 获取本月第一天到今天的日期
            $firstDay = date("Y-m") . '-01'; //    2020-12-01
            $today = date("Y-m-d", time()); //     2020-12-17

        } elseif ($params['range'] == 'week') {
            // 获取本周第一天到今天的日期
            $firstDay = Carbon::now()->startOfWeek()->toDateString(); //  2020-12-14
            $today = date("Y-m-d", time()); // 今天日期     2020-12-17

        } elseif ($params['range'] == 'custom') {
            $firstDay =  trim(substr($params['date_range'], 0, 10));    // 2020-12-01
            $today =  trim(substr($params['date_range'], -10));    // 2020-12-31
        }

        $date = [$firstDay.' 00:00:00', $today.' 23:59:59'];
        $res = $this -> getSalesVolumePerShop($date);

        // 对结果按销售额进行排序
        $res = $this -> arr -> arraySort($res, 'total_price', 'desc');
        $getHeadTen = array_slice($res,0,10);

        return ['code' => ErrorCode::SUCCESS, 'data' => $getHeadTen];
    }

    /**
     * 获取每个店铺的销售额（不包括退款）
     * @param $date
     * @return array
     */
    public function getSalesVolumePerShop($date)
    {
        $totalPerShop = OrderModel::query()
            ->from('store_order as o')
            ->leftJoin('store_shop as s', 'o.shop_id', '=', 's.shop_id')
            ->selectRaw("
                  o.shop_id,
                  SUM(o.total_price) as total_price,
                  s.shop_name
            ")
            -> when($date ?? 0, function ($query, $date) {
                return $query->whereBetween('pay_at', $date);
            })
            ->where('is_pay', 1)
            ->where('order_type',9)
            ->groupBy(['shop_id'])
            ->get('total_price', 'shop_id')->toArray();

        $totalPerShopWithIndex = array_column($totalPerShop, null, 'shop_id');
        $refundPerShop = $this -> getSeckillRefundPerShop($date);

        // 每个门店的销售额减去退款
        foreach ($totalPerShopWithIndex as $key => $val)
        {
            if(array_key_exists($key, $refundPerShop)){
                $totalPerShopWithIndex[$key]['total_price'] = bcsub((string)$totalPerShopWithIndex[$key]['total_price'], (string)$refundPerShop[$key], 2);
            }
        }

        return $totalPerShopWithIndex;
    }

    /**
     * 每个门店的退款额
     * @param null $date
     * @return array
     *
     * 说明：客至退款 + 吾享退款
     */
    public function getSeckillRefundPerShop($date = null)
    {
        // 查出每笔订单的退款
        $kzRefund = KzRefundLogModel::query()
            -> selectRaw("
                shop_id,
                SUM(refund_balance_money) as refund_money
            ")
            -> when($date ?? 0, function ($query, $date) {
                return $query->whereBetween('create_time', $date);
            })
            -> where('order_type', 9)
            -> groupBy(['shop_id'])
            -> pluck('refund_money', 'shop_id')->toArray();

        $wxRefund = WxRefundLogModel::query()
            -> selectRaw("
                shop_id,
                SUM(refund_wx_money) as refund_money
            ")
            -> when($date ?? 0, function ($query, $date) {
                return $query->whereBetween('create_time', $date);
            })
            -> where('order_type', 9)
            -> groupBy(['shop_id'])
            -> pluck('refund_money', 'shop_id')->toArray();

        return $this -> arrayAdd($kzRefund, $wxRefund);
    }

    /**
     * 活动数据分析列表
     * @param array $params
     * @return array
     */
    public function activityDataAnalysis(array $params = [])
    {
        $shopId = $params['shop_id'] ?? null;
        $shopGroupId = $params['shop_group_id'] ?? null;
        $actIds = null;
        if($shopId){
            $actIds = ActivityModel::query()
                ->where('shop_ids', 'regexp', $shopId)
                ->where('activityType', 4)
                ->where('is_deleted',0)
                ->pluck('activityID')->toArray();

        }elseif ($shopGroupId){
            $shopId = ShopModel::query()
                ->where('shop_group_id', $shopGroupId)
                ->where('is_deleted', 0)
                ->pluck('shop_id') -> toArray();

            $in = [
//                ['shop_ids', ['20200529274466', '20200606173091']],
                ['shop_ids', $shopId],
            ];

            $actIds = ActivityModel::query()
                ->where('activityType', 4)
                ->where('is_deleted',0)
                ->Where(function ($query) use ($in){
                    foreach ($in as $k => $v) {
                        foreach($v[1] as $key => $val){
                            $query->orWhere($v[0],'regexp', $val);
//                            $query->orWhereRaw("FIND_IN_SET(?, $v[0])", [$val]);
                        }
                    }
                    return $query;
                })
                ->pluck('activityID')->toArray();

        }

        // 查出所有的秒杀活动
        $activityList = ActivityModel::query()
            ->selectRaw("
                activityID,
                title,
                DATE_FORMAT(begin_date, '%Y-%m-%d') as date,
                activity_prams,
                create_at,
                shop_ids
            ")
            -> when($actIds ?? 0, function ($query, $actIds) {
                return $query->whereIn('activityID', $actIds);
            })
            ->where('activityType', 4)
            ->where('is_deleted', 0)
            ->get() -> toArray();

        $activityIds = array_column($activityList,'activityID');

        // 每个活动的商品数和商品点击量
        $activityGoods = ActivityGoodsModel::query()
            ->whereIn('activityID', $activityIds)
            ->selectRaw('COUNT(goods_id) as goods_num,activityID,SUM(click_num) as click_num')
            ->groupBy(['activityID'])
            ->get()->toArray();

        $activityGoodsIndex = array_column($activityGoods, null, 'activityID');

        // 获取每个活动的订单数量
        $orderNum = $this -> seckillOrderNumPerAct($activityIds);

        // 销售数量（不含退款）
        $salesVolumePerAct = $this -> seckillSalesVolumePerAct($activityIds);

        // 销售额（不含退货）
        $salesAmountPerAct = $this -> seckillSalesAmountPerAct($activityIds);


//        var_dump($salesVolumePerAct);die;

        // 退货金额--秒杀订单的退货金额合计
        $refundAmount = $this -> refundAmountPerAct($activityIds);

        // 退货数量
        $refundNum = OrderModel::query()
            -> from('store_order as o')
            -> leftJoin('store_order_goods as og', 'o.order_no', '=', 'og.order_no')
            -> selectRaw("
                 o.activity_id,
                 (SUM(og.refund_number) + SUM(og.shop_refund_number)) as refund_amount
            ")
            ->where('og.refund_at', '<>', null)
            -> whereIn('o.activity_id', $activityIds)
            -> groupBy(['o.activity_id'])
            -> pluck('refund_amount', 'activity_id') -> toArray();

        // 组装最后的列表数据
        foreach($activityList as $key => $val){
            $activityList[$key]['shop_count'] = count(explode(',', $val['shop_ids']));  // 应用商品数
            $activityList[$key]['goods_count'] = $activityGoodsIndex[$val['activityID']]['goods_num'];    // 活动商品数
            $activityList[$key]['activity_click_num'] = $activityGoodsIndex[$val['activityID']]['click_num'];    // 活动点击量
            $timeRange = json_decode($val['activity_prams'], true);
            $activityList[$key]['activity_time'] = $val['date'].' '.$timeRange['start_time'].'-'.$timeRange['end_time'];    // 活动时间
            $activityList[$key]['sales_volume'] = $salesVolumePerAct[$val['activityID']]['sales_volumes'] ?? 0;  // 销售数量
            $activityList[$key]['total_price'] = $salesAmountPerAct[$val['activityID']]['total_price'] ?? '0.00';  // 销售额
            $activityList[$key]['order_num'] = $orderNum[$val['activityID']] ?? 0;  // 订单数量
            $activityList[$key]['refund_num'] = $refundNum[$val['activityID']] ?? 0;  // 退货数量
            $activityList[$key]['refund_price'] = $refundAmount[$val['activityID']] ?? '0.00';  // 退货数量
        }

        return ['code' => ErrorCode::SUCCESS, 'data' => $activityList];
    }

    /**
     * 每个秒杀活动商品的销售量（不含退货量）
     * @param array $activityIds
     * @return array
     */
    public function seckillSalesVolumePerAct(array $activityIds)
    {
        // 查出所有秒杀活动付过款的商品数量和订单金额
        $payNum = OrderModel::query()
            ->from('store_order as o')
            ->leftJoin('store_order_goods as og','o.order_no','=','og.order_no')
            ->selectRaw("
                o.order_no,
                o.activity_id,
                SUM(og.number) as sales_volumes
            ")
            ->where('o.is_pay', 1)
            ->where('o.order_type', 9)
            ->when($activityIds ?? 0, function ($query, $activityIds) {
                return $query->whereIn('o.activity_id', $activityIds);
            })
            ->groupBy(['o.activity_id'])
            ->get()->toArray();

        // 查出所有秒杀活动的退货商品数量
        $refundNum = OrderModel::query()
            ->from('store_order as o')
            ->leftJoin('store_order_goods as og','o.order_no','=','og.order_no')
            ->selectRaw("
                o.activity_id,
                (SUM(og.refund_number) + SUM(og.shop_refund_number)) as refund_amount
            ")
            ->when($activityIds ?? 0, function ($query, $activityIds) {
                return $query->whereIn('o.activity_id', $activityIds);
            })
            ->where('og.refund_at', '<>', null)
            ->where('o.is_pay', 1)
            ->where('o.order_type', 9)
            ->groupBy(['o.activity_id'])
            ->pluck('refund_amount', 'activity_id')->toArray();

        // 销售量减去退货量
        foreach ($payNum as $key => $val)
        {
            if(array_key_exists($val['activity_id'], $refundNum)){
                $payNum[$key]['sales_volumes'] = bcsub((string)$payNum[$key]['sales_volumes'], (string)$refundNum[$val['activity_id']]);
            }
        }

        $res = array_column($payNum, null, 'activity_id');

        return $res;
    }

    /**
     * 每个秒杀活动的订单量，包含未退款的和部分退的订单
     * @param null $activityIds
     * @return array
     */
    public function seckillOrderNumPerAct($activityIds = null)
    {
        // 未退款订单
        $count1 = OrderModel::query()
            ->selectRaw("
            activity_id,
            COUNT(*) as sale_order
            ")
            ->where('is_pay', 1)
            ->where('order_type', 9)
            ->where('refund_at', null)
            ->when($activityIds ?? 0, function ($query, $activityIds) {
                return $query->whereIn('activity_id', $activityIds);
            })
            ->groupBy(['activity_id'])
            ->pluck('sale_order', 'activity_id')->toArray();

        // 部分退款订单
        $count2 = OrderModel::query()
            ->selectRaw("
            activity_id,
            count(*) as sale_order
            ")
            ->where('is_pay', 1)
            ->where('order_type', 9)
            ->where('is_whole', 1)
            ->when($activityIds ?? 0, function ($query, $activityIds) {
                return $query->whereIn('activity_id', $activityIds);
            })
            ->groupBy(['activity_id'])
            ->pluck('sale_order', 'activity_id')->toArray();

        return $this -> arrayAdd($count1, $count2);
    }

    /**
     * 每个秒杀活动商品的销售额（不含退款）
     * @param array $activityIds
     * @return array
     */
    public function seckillSalesAmountPerAct(array $activityIds)
    {
        // 查出所有秒杀活动付过款的订单金额
        $payAmount = OrderModel::query()
            ->selectRaw("
                order_no,
                activity_id,
                SUM(total_price) as total_price
            ")
            ->where('is_pay', 1)
            ->where('order_type', 9)
            ->when($activityIds ?? 0, function ($query, $activityIds) {
                return $query->whereIn('activity_id', $activityIds);
            })
            ->groupBy(['activity_id'])
            ->get()->toArray();

        // 查出所有商品的退款金额
        $refundAmount = $this -> refundAmountPerAct($activityIds);
        // 销售额减去退款金额
        foreach ($payAmount as $key => $val)
        {
            if(array_key_exists($val['activity_id'], $refundAmount)){
                $payAmount[$key]['total_price'] = bcsub((string)$payAmount[$key]['total_price'], (string)$refundAmount[$val['activity_id']], 2);
            }
        }
        $res = array_column($payAmount, null, 'activity_id');

        return $res;
    }

    /**
     * 每个活动的退款金额
     * @param array $activityIds
     * @return array
     */
    public function refundAmountPerAct(array $activityIds)
    {
        // 获取每个秒杀活动下的订单
        $orderIds = OrderModel::query()
            ->whereIn('activity_id', $activityIds)
            ->where('order_type',9)
            ->get(['order_no','activity_id'])->toArray();

        // 将同一个秒杀活动的订单集合在一起
        $actWithOrderIds = $this -> arr -> focusValBySameKey($orderIds, 'activity_id', 'order_no');

        // 查询每个活动的退款金额
        $arr = [];
        foreach($actWithOrderIds as $key => $val){
            $refundAmount = $this -> refundAmountByOrders($val);
            if($refundAmount > 0){
                $arr[$key] = $refundAmount;
            }
        }

        return $arr;

    }


    /**
     * @param array $orders
     * @return string
     */
    public function refundAmountByOrders(array $orders = [])
    {
        $kzRefund = KzRefundLogModel::query()
            -> selectRaw("
                SUM(refund_balance_money) as refund_money
            ")
            -> when($orders ?? 0, function ($query, $orders) {
                return $query->whereIn('order_no', $orders);
            })
            -> where('order_type', 9)
            -> value('refund_money');

        $wxRefund = WxRefundLogModel::query()
            -> selectRaw("
                SUM(refund_wx_money) as refund_money
            ")
            -> when($orders ?? 0, function ($query, $orders) {
                return $query->whereIn('order_no', $orders);
            })
            -> where('order_type', 9)
            -> value('refund_money');

        return bcadd((string)$kzRefund, (string)$wxRefund, 2);
    }

    /**
     * 店铺秒杀缓存
     * 后台秒杀的增删改查 以切面的方式更新缓存
     * @author liule
     */
    public function generateSecKillActDataForEveryStore()
    {
        // 所有店铺id
        $shopIds = ShopModel::query()
            ->where('is_deleted', 0)
            ->pluck('shop_id')
            ->toArray();
        if (!empty($shopIds)){
            $map = [];
            $parallel = new Parallel(50);
            foreach ($shopIds as $k => $v){
                $parallel->add(function () use($v, $map) {
                    $res = $this->SecKillActAll((string)$v);
                    $map[$v] = json_encode($res);
                    return $map;
                });
            }
            $results = $parallel->wait();
            $seckill = [];
            foreach ($results as $k => $v){
                foreach ($v as $kk => $vv){
                    if (empty(json_decode($vv,true))) continue 2;
                    $seckill = $seckill + [$kk => $vv];
                }
            }
            $key = 'seckill:' . date('Ymd');
            $redis = $this->seckillRedis;
            $redis->hMSet($key, $seckill);
            $ttl = $redis->ttl($key);
            if (in_array($ttl, [-1, -2])){
                $diff = strtotime(date('Y-m-d 23:59:59')) - time();
                $redis->expire($key, $diff);
            }
        }

    }

    /**
     * 获取活动id  分享
     * @param int $activityGoodsId
     * @return \Hyperf\Utils\HigherOrderTapProxy|mixed|void|null
     */
    public function getActivityId(int $activityGoodsId)
    {
        return ActivityGoodsModel::where(['id' => $activityGoodsId])->value('activityID');
    }

    /***
     * 获取活动商品  分享
     * 包含无效活动
     * @param array $activityGoodsId
     * @return array
     */
    public function getActivityGoodsList(array $activityGoodsId)
    {
        $field = [
            'ag.goods_title as title',
            'a.begin_date',
            'a.end_date',
            'a.shop_ids',
            'a.activity_prams',
            'a.status',
            'a.is_deleted',
            'a.image',
            'g.logo',
            'ag.goods_id',
            'ag.goods_spec',
            'ag.stock',
            'ag.per_can_buy_num as limite_num_per_day',
            'ag.commission',
            'ag.group_id',
            'ag.costprice as price_market',
            'ag.price_selling',
            'ag.current_cost_price as cost_price',
            'ag.id as activity_goods_id'
        ];
        $activityGoods = ActivityModel::query()
            ->from('store_activity as a')
            ->join('store_activity_goods as ag', 'a.activityID', '=', 'ag.activityID')
            ->join('store_goods as g','ag.goods_id','=','g.id')
            ->select($field)
            ->whereIn('ag.id', $activityGoodsId)
            ->get()->toArray();
        return $activityGoods ;
    }
    /**
     * 获取活动商品信息
     * @param int $activityId
     * @return array
     * @author lulongfei
     */
    public function getActivityGoodsInfo(int $activityId, int $shop_id,int $order_type=6)
    {
        $field = [
            's.activityID',
            's.activityType',
            's.activity_prams',
            'ag.id as activity_goods_id',
            'g.introduction',
            'ag.spell_num',
            'ag.group_id',
            'ag.stock',
            'ag.commission',
            's.begin_date',
            's.end_date',
            'g.pick_time',
            'g.cate_id',
            'g.kz_goods_id',
            'g.id as goods_id',
            'g.title as goods_title',
            's.image',
            'ag.goods_spec',
            'ag.costprice',
            'ag.price_selling',
            'ag.current_cost_price',
        ];
        $where = [
            's.activityID' => $activityId,
            's.is_deleted' => 0,
            's.status' => 1,
        ];
        if($order_type==10){
            $where['s.status'] = 2;
        }
        $goodsByActivity = ActivityModel::query()
            ->from('store_activity as s')
            ->join('store_activity_goods as ag', 's.activityID', '=', 'ag.activityID')
            ->join('store_goods as g', 'ag.goods_id', '=', 'g.id')
            ->where($where)
            ->whereRaw('FIND_IN_SET(?,s.shop_ids)', [$shop_id])
            ->select($field)->first();

        if($goodsByActivity){
            $goodsByActivity =  $goodsByActivity->toarray();
        }else{
            return [];
        }
        $goodsByActivity['begin_date'] = strtotime($goodsByActivity['begin_date']);
        $goodsByActivity['end_date'] = strtotime($goodsByActivity['end_date']);
        $goodsByActivity['group_num'] = $goodsByActivity['activityType'] == 0 ? json_decode($goodsByActivity['activity_prams'], true)['group_num'] : 0;
        return $goodsByActivity;
    }
    /**
     * 门店参加活动商品ID
     * @param int $shop_id
     * @return array
     */
    public static function getActivityGoodsByShopId(int $shop_id)
    {
        $goodsIds =  ActivityModel::query()
            ->from('store_activity as ac')
            ->leftJoin('store_activity_goods as ag', 'ac.activityID', '=', 'ag.activityID')
            ->where('ac.end_date', '>', date('Y-m-d H:i:s'))
            ->where('ac.status', '!=',0)
            ->whereRaw('FIND_IN_SET(?,ac.shop_ids)', [$shop_id])
            ->pluck('ag.goods_id');
        return array_filter(array_unique($goodsIds->toArray()));
    }

    /**
     * 查询所有店铺活动商品数据
     * @param string $type
     * @return array
     * @author 1iu
     * @date 2021-02-06 17:10
     */
    public function getActGoodsIdsByAllShop(string $type = ''):array {
        return ActivityModel::query()
            ->from('store_activity as ac')
            ->leftJoin('store_activity_goods as ag', 'ac.activityID', '=', 'ag.activityID')
            ->select(['ag.goods_id', 'ac.shop_ids'])
            ->when(empty($type), function ($query){
                return $query->where('ac.end_date', '>', date('Y-m-d H:i:s'));
            }, function ($query) use($type) {
                if ($type == 'seckill'){
                    $query->where([
                        ['ac.activityType', '=' , 4],
                        ['ac.status', '<>' , 0],
                        ['ac.is_deleted', '=' , 0],
                    ])->whereDate('ac.begin_date',date('Y-m-d',time()));
                }
                return $query;
            })->get()->toArray();
    }

    /**
     *当前店铺有效活动商品
     * @param array $activityIds
     * @param array $activityGoodsId
     * @param int $shop_id
     * @return array
     */
    public function activeGoods(array $activityIds, array $activityGoodsId, int $shop_id)
    {
        return ActivityModel::query()->from('store_activity_goods as g')
            ->join('store_activity as s','s.activityID','=', 'g.activityID')
            ->join('store_goods as t','t.id','=','g.goods_id')
            ->where(['g.is_deleted' => 0, 's.is_deleted' => 0,'g.group_id' => 3])
            ->where('g.stock','>',0)
            ->where('s.status','<>',0)
            ->where('s.end_date','>=',date('Y-m-d H:i:s'))
            ->where(['t.is_deleted' => 0, 't.status' => 1 ,'t.is_today' => '1'])
            ->whereIn('g.id',$activityIds)
            ->whereIn('t.id',$activityGoodsId)
            ->whereRaw('!FIND_IN_SET(?,t.shop_ids)', [$shop_id])
            ->whereRaw('FIND_IN_SET(?,s.shop_ids)', [$shop_id])
            ->pluck('g.id')
            ->toArray();
    }

    /**
     * 当前活动的每人限购数量
     * @param array $where
     * @param string $column
     * @param array $whereIn
     * @param array $filed
     * @return array
     */
    public function perBuyNum(array $where, string $column, array $whereIn, array $filed)
    {
        return ActivityGoodsModel::query()->where($where)->whereIn($column,$whereIn)->select($filed)->get()->toArray();
    }

    /**
     * 活动商品信息
     * @param array $where
     * @param string $column
     * @param array $whereIn
     * @param array $field
     * @return array
     */
    public function activityBlurb(array $where, string $column, array $whereIn, array $field)
    {
       return ActivityGoodsModel::query()->where($where)->whereIn($column,$whereIn)->select($field)->get()->toArray();
    }
}
